diff options
Diffstat (limited to 'ext/standard/array.c')
| -rw-r--r-- | ext/standard/array.c | 4650 |
1 files changed, 2862 insertions, 1788 deletions
diff --git a/ext/standard/array.c b/ext/standard/array.c index 69ebfee89a..30ca0508a9 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1,6 +1,6 @@ -/* +/* +----------------------------------------------------------------------+ - | PHP Version 5 | + | PHP Version 7 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2016 The PHP Group | +----------------------------------------------------------------------+ @@ -45,7 +45,7 @@ #include "basic_functions.h" #include "php_string.h" #include "php_rand.h" -#include "php_smart_str.h" +#include "zend_smart_str.h" #ifdef HAVE_SPL #include "ext/spl/spl_array.h" #endif @@ -141,93 +141,561 @@ PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */ } /* }}} */ -static void php_set_compare_func(int sort_type TSRMLS_DC) /* {{{ */ +static int php_array_key_compare(const void *a, const void *b) /* {{{ */ { - switch (sort_type & ~PHP_SORT_FLAG_CASE) { - case PHP_SORT_NUMERIC: - ARRAYG(compare_func) = numeric_compare_function; - break; + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + zend_uchar t; + zend_long l1, l2; + double d; + + if (f->key == NULL) { + if (s->key == NULL) { + return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + } else { + l1 = (zend_long)f->h; + t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1); + if (t == IS_LONG) { + /* pass */ + } else if (t == IS_DOUBLE) { + return ZEND_NORMALIZE_BOOL((double)l1 - d); + } else { + l2 = 0; + } + } + } else { + if (s->key) { + return zendi_smart_strcmp(f->key, s->key); + } else { + l2 = (zend_long)s->h; + t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1); + if (t == IS_LONG) { + /* pass */ + } else if (t == IS_DOUBLE) { + return ZEND_NORMALIZE_BOOL(d - (double)l2); + } else { + l1 = 0; + } + } + } + return l1 > l2 ? 1 : (l1 < l2 ? -1 : 0); +} +/* }}} */ - case PHP_SORT_STRING: - ARRAYG(compare_func) = sort_type & PHP_SORT_FLAG_CASE ? string_case_compare_function : string_compare_function; - break; +static int php_array_reverse_key_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare(b, a); +} +/* }}} */ - case PHP_SORT_NATURAL: - ARRAYG(compare_func) = sort_type & PHP_SORT_FLAG_CASE ? string_natural_case_compare_function : string_natural_compare_function; - break; +static int php_array_key_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; -#if HAVE_STRCOLL - case PHP_SORT_LOCALE_STRING: - ARRAYG(compare_func) = string_locale_compare_function; - break; -#endif + if (f->key == NULL && s->key == NULL) { + return (zend_long)f->h > (zend_long)s->h ? 1 : -1; + } else { + double d1, d2; + if (f->key) { + d1 = zend_strtod(f->key->val, NULL); + } else { + d1 = (double)(zend_long)f->h; + } + if (s->key) { + d2 = zend_strtod(s->key->val, NULL); + } else { + d2 = (double)(zend_long)s->h; + } + return ZEND_NORMALIZE_BOOL(d1 - d2); + } +} +/* }}} */ - case PHP_SORT_REGULAR: - default: - ARRAYG(compare_func) = compare_function; - break; +static int php_array_reverse_key_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_numeric(b, a); +} +/* }}} */ + +static int php_array_key_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + size_t l1, l2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + l1 = f->key->len; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + l1 = buf1 + sizeof(buf1) - 1 - s1; + } + if (s->key) { + s2 = s->key->val; + l2 = s->key->len; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + l2 = buf2 + sizeof(buf2) - 1 - s1; } + return zend_binary_strcasecmp_l(s1, l1, s2, l2); } /* }}} */ -static int php_array_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ +static int php_array_reverse_key_compare_string_case(const void *a, const void *b) /* {{{ */ { - Bucket *f; - Bucket *s; - zval result; - zval first; - zval second; + return php_array_key_compare_string_case(b, a); +} +/* }}} */ + +static int php_array_key_compare_string(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + size_t l1, l2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + l1 = f->key->len; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + l1 = buf1 + sizeof(buf1) - 1 - s1; + } + if (s->key) { + s2 = s->key->val; + l2 = s->key->len; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + l2 = buf2 + sizeof(buf2) - 1 - s1; + } + return zend_binary_strcmp(s1, l1, s2, l2); +} +/* }}} */ - f = *((Bucket **) a); - s = *((Bucket **) b); +static int php_array_reverse_key_compare_string(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string(b, a); +} +/* }}} */ - if (f->nKeyLength == 0) { - Z_TYPE(first) = IS_LONG; - Z_LVAL(first) = f->h; +static int php_array_key_compare_string_natural_general(const void *a, const void *b, int fold_case) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + size_t l1, l2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + l1 = f->key->len; } else { - Z_TYPE(first) = IS_STRING; - Z_STRVAL(first) = (char*)f->arKey; - Z_STRLEN(first) = f->nKeyLength - 1; + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + l1 = buf1 + sizeof(buf1) - 1 - s1; } + if (s->key) { + s2 = s->key->val; + l2 = s->key->len; + } else { + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); + l2 = buf2 + sizeof(buf2) - 1 - s1; + } + return strnatcmp_ex(s1, l1, s2, l2, fold_case); +} +/* }}} */ + +static int php_array_key_compare_string_natural_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(a, b, 1); +} +/* }}} */ + +static int php_array_reverse_key_compare_string_natural_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(b, a, 1); +} +/* }}} */ + +static int php_array_key_compare_string_natural(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(a, b, 0); +} +/* }}} */ - if (s->nKeyLength == 0) { - Z_TYPE(second) = IS_LONG; - Z_LVAL(second) = s->h; +static int php_array_reverse_key_compare_string_natural(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_natural_general(b, a, 0); +} +/* }}} */ + +#if HAVE_STRCOLL +static int php_array_key_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + char *s1, *s2; + char buf1[MAX_LENGTH_OF_LONG + 1]; + char buf2[MAX_LENGTH_OF_LONG + 1]; + + if (f->key) { + s1 = f->key->val; + } else { + s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h); + } + if (s->key) { + s2 = s->key->val; } else { - Z_TYPE(second) = IS_STRING; - Z_STRVAL(second) = (char*)s->arKey; - Z_STRLEN(second) = s->nKeyLength - 1; + s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h); } + return strcoll(s1, s2); +} +/* }}} */ + +static int php_array_reverse_key_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + return php_array_key_compare_string_locale(b, a); +} +/* }}} */ +#endif + +/* Numbers are always smaller than strings int this function as it + * anyway doesn't make much sense to compare two different data types. + * This keeps it consistent and simple. + * + * This is not correct any more, depends on what compare_func is set to. + */ +static int php_array_data_compare(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval result; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; - if (ARRAYG(compare_func)(&result, &first, &second TSRMLS_CC) == FAILURE) { + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + if (compare_function(&result, first, second) == FAILURE) { return 0; } - if (Z_TYPE(result) == IS_DOUBLE) { - if (Z_DVAL(result) < 0) { - return -1; - } else if (Z_DVAL(result) > 0) { - return 1; - } else { - return 0; - } + ZEND_ASSERT(Z_TYPE(result) == IS_LONG); + return Z_LVAL(result); +} +/* }}} */ + +static int php_array_reverse_data_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare(a, b) * -1; +} +/* }}} */ + +static int php_array_data_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + + return numeric_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_numeric(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_numeric(b, a); +} +/* }}} */ + +static int php_array_data_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; - convert_to_long(&result); + first = &f->val; + second = &s->val; - if (Z_LVAL(result) < 0) { - return -1; - } else if (Z_LVAL(result) > 0) { - return 1; + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); } - return 0; + return string_case_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_string_case(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_string_case(b, a); +} +/* }}} */ + +static int php_array_data_compare_string(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + + return string_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_string(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_string(b, a); +} +/* }}} */ + +static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */ +{ + Bucket *f = (Bucket *) a; + Bucket *s = (Bucket *) b; + zend_string *str1 = zval_get_string(&f->val); + zend_string *str2 = zval_get_string(&s->val); + + int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case); + + zend_string_release(str1); + zend_string_release(str2); + return result; +} +/* }}} */ + +static int php_array_natural_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(a, b, 0); } /* }}} */ -static int php_array_reverse_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ +static int php_array_reverse_natural_compare(const void *a, const void *b) /* {{{ */ { - return php_array_key_compare(a, b TSRMLS_CC) * -1; + return php_array_natural_general_compare(b, a, 0); +} +/* }}} */ + +static int php_array_natural_case_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(a, b, 1); +} +/* }}} */ + +static int php_array_reverse_natural_case_compare(const void *a, const void *b) /* {{{ */ +{ + return php_array_natural_general_compare(b, a, 1); +} +/* }}} */ + +#if HAVE_STRCOLL +static int php_array_data_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + Bucket *f; + Bucket *s; + zval *first; + zval *second; + + f = (Bucket *) a; + s = (Bucket *) b; + + first = &f->val; + second = &s->val; + + if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) { + first = Z_INDIRECT_P(first); + } + if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) { + second = Z_INDIRECT_P(second); + } + + return string_locale_compare_function(first, second); +} +/* }}} */ + +static int php_array_reverse_data_compare_string_locale(const void *a, const void *b) /* {{{ */ +{ + return php_array_data_compare_string_locale(b, a); +} +/* }}} */ +#endif + +static compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */ +{ + switch (sort_type & ~PHP_SORT_FLAG_CASE) { + case PHP_SORT_NUMERIC: + if (reverse) { + return php_array_reverse_key_compare_numeric; + } else { + return php_array_key_compare_numeric; + } + break; + + case PHP_SORT_STRING: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_key_compare_string_case; + } else { + return php_array_key_compare_string_case; + } + } else { + if (reverse) { + return php_array_reverse_key_compare_string; + } else { + return php_array_key_compare_string; + } + } + break; + + case PHP_SORT_NATURAL: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_key_compare_string_natural_case; + } else { + return php_array_key_compare_string_natural_case; + } + } else { + if (reverse) { + return php_array_reverse_key_compare_string_natural; + } else { + return php_array_key_compare_string_natural; + } + } + break; + +#if HAVE_STRCOLL + case PHP_SORT_LOCALE_STRING: + if (reverse) { + return php_array_reverse_key_compare_string_locale; + } else { + return php_array_key_compare_string_locale; + } + break; +#endif + + case PHP_SORT_REGULAR: + default: + if (reverse) { + return php_array_reverse_key_compare; + } else { + return php_array_key_compare; + } + break; + } + return NULL; +} +/* }}} */ + +static compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */ +{ + switch (sort_type & ~PHP_SORT_FLAG_CASE) { + case PHP_SORT_NUMERIC: + if (reverse) { + return php_array_reverse_data_compare_numeric; + } else { + return php_array_data_compare_numeric; + } + break; + + case PHP_SORT_STRING: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_data_compare_string_case; + } else { + return php_array_data_compare_string_case; + } + } else { + if (reverse) { + return php_array_reverse_data_compare_string; + } else { + return php_array_data_compare_string; + } + } + break; + + case PHP_SORT_NATURAL: + if (sort_type & PHP_SORT_FLAG_CASE) { + if (reverse) { + return php_array_reverse_natural_case_compare; + } else { + return php_array_natural_case_compare; + } + } else { + if (reverse) { + return php_array_reverse_natural_compare; + } else { + return php_array_natural_compare; + } + } + break; + +#if HAVE_STRCOLL + case PHP_SORT_LOCALE_STRING: + if (reverse) { + return php_array_reverse_data_compare_string_locale; + } else { + return php_array_data_compare_string_locale; + } + break; +#endif + + case PHP_SORT_REGULAR: + default: + if (reverse) { + return php_array_reverse_data_compare; + } else { + return php_array_data_compare; + } + break; + } + return NULL; } /* }}} */ @@ -236,15 +704,24 @@ static int php_array_reverse_key_compare(const void *a, const void *b TSRMLS_DC) PHP_FUNCTION(krsort) { zval *array; - long sort_type = PHP_SORT_REGULAR; + zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } +#else + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY_EX(array, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sort_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#endif - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_key_compare_func(sort_type, 1); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_key_compare, 0 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -256,43 +733,52 @@ PHP_FUNCTION(krsort) PHP_FUNCTION(ksort) { zval *array; - long sort_type = PHP_SORT_REGULAR; + zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } +#else + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY_EX(array, 0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(sort_type) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#endif - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_key_compare_func(sort_type, 0); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_key_compare, 0 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ -PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ +PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */ { - long cnt = 0; - zval **element; + zend_long cnt = 0; + zval *element; if (Z_TYPE_P(array) == IS_ARRAY) { - if (Z_ARRVAL_P(array)->nApplyCount > 1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); + if (Z_ARRVAL_P(array)->u.v.nApplyCount > 1) { + php_error_docref(NULL, E_WARNING, "recursion detected"); return 0; } - cnt = zend_hash_num_elements(Z_ARRVAL_P(array)); + cnt = zend_array_count(Z_ARRVAL_P(array)); if (mode == COUNT_RECURSIVE) { - HashPosition pos; - - for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); - zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS; - zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos) - ) { - Z_ARRVAL_P(array)->nApplyCount++; - cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC); - Z_ARRVAL_P(array)->nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { + Z_ARRVAL_P(array)->u.v.nApplyCount++; + } + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) { + ZVAL_DEREF(element); + cnt += php_count_recursive(element, COUNT_RECURSIVE); + } ZEND_HASH_FOREACH_END(); + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { + Z_ARRVAL_P(array)->u.v.nApplyCount--; } } } @@ -306,37 +792,53 @@ PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ PHP_FUNCTION(count) { zval *array; - long mode = COUNT_NORMAL; + zend_long mode = COUNT_NORMAL; + zend_long cnt; + zval *element; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|l", &array, &mode) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(array) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(mode) + ZEND_PARSE_PARAMETERS_END(); +#endif switch (Z_TYPE_P(array)) { case IS_NULL: RETURN_LONG(0); break; case IS_ARRAY: - RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC)); + cnt = zend_array_count(Z_ARRVAL_P(array)); + if (mode == COUNT_RECURSIVE) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) { + ZVAL_DEREF(element); + cnt += php_count_recursive(element, COUNT_RECURSIVE); + } ZEND_HASH_FOREACH_END(); + } + RETURN_LONG(cnt); break; case IS_OBJECT: { #ifdef HAVE_SPL - zval *retval; + zval retval; #endif /* first, we check if the handler is defined */ if (Z_OBJ_HT_P(array)->count_elements) { RETVAL_LONG(1); - if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value) TSRMLS_CC)) { + if (SUCCESS == Z_OBJ_HT(*array)->count_elements(array, &Z_LVAL_P(return_value))) { return; } } #ifdef HAVE_SPL /* if not and the object implements Countable we call its count() method */ - if (Z_OBJ_HT_P(array)->get_class_entry && instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) { - zend_call_method_with_0_params(&array, NULL, NULL, "count", &retval); - if (retval) { - convert_to_long_ex(&retval); - RETVAL_LONG(Z_LVAL_P(retval)); + if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) { + zend_call_method_with_0_params(array, NULL, NULL, "count", &retval); + if (Z_TYPE(retval) != IS_UNDEF) { + RETVAL_LONG(zval_get_long(&retval)); zval_ptr_dtor(&retval); } return; @@ -350,123 +852,20 @@ PHP_FUNCTION(count) } /* }}} */ -/* Numbers are always smaller than strings int this function as it - * anyway doesn't make much sense to compare two different data types. - * This keeps it consistent and simple. - * - * This is not correct any more, depends on what compare_func is set to. - */ -static int php_array_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ -{ - Bucket *f; - Bucket *s; - zval result; - zval *first; - zval *second; - - f = *((Bucket **) a); - s = *((Bucket **) b); - - first = *((zval **) f->pData); - second = *((zval **) s->pData); - - if (ARRAYG(compare_func)(&result, first, second TSRMLS_CC) == FAILURE) { - return 0; - } - - if (Z_TYPE(result) == IS_DOUBLE) { - if (Z_DVAL(result) < 0) { - return -1; - } else if (Z_DVAL(result) > 0) { - return 1; - } else { - return 0; - } - } - - convert_to_long(&result); - - if (Z_LVAL(result) < 0) { - return -1; - } else if (Z_LVAL(result) > 0) { - return 1; - } - - return 0; -} -/* }}} */ - -static int php_array_reverse_data_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ -{ - return php_array_data_compare(a, b TSRMLS_CC) * -1; -} -/* }}} */ - -static int php_array_natural_general_compare(const void *a, const void *b, int fold_case) /* {{{ */ -{ - Bucket *f, *s; - zval *fval, *sval; - zval first, second; - int result; - - f = *((Bucket **) a); - s = *((Bucket **) b); - - fval = *((zval **) f->pData); - sval = *((zval **) s->pData); - first = *fval; - second = *sval; - - if (Z_TYPE_P(fval) != IS_STRING) { - zval_copy_ctor(&first); - convert_to_string(&first); - } - - if (Z_TYPE_P(sval) != IS_STRING) { - zval_copy_ctor(&second); - convert_to_string(&second); - } - - result = strnatcmp_ex(Z_STRVAL(first), Z_STRLEN(first), Z_STRVAL(second), Z_STRLEN(second), fold_case); - - if (Z_TYPE_P(fval) != IS_STRING) { - zval_dtor(&first); - } - - if (Z_TYPE_P(sval) != IS_STRING) { - zval_dtor(&second); - } - - return result; -} -/* }}} */ - -static int php_array_natural_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ -{ - return php_array_natural_general_compare(a, b, 0); -} -/* }}} */ - -static int php_array_natural_case_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ -{ - return php_array_natural_general_compare(a, b, 1); -} -/* }}} */ - static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */ { zval *array; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &array) == FAILURE) { return; } if (fold_case) { - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_case_compare, 0 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0) == FAILURE) { return; } } else { - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_natural_compare, 0 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0) == FAILURE) { return; } } @@ -496,15 +895,16 @@ PHP_FUNCTION(natcasesort) PHP_FUNCTION(asort) { zval *array; - long sort_type = PHP_SORT_REGULAR; + zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_data_compare_func(sort_type, 0); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 0 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -516,15 +916,16 @@ PHP_FUNCTION(asort) PHP_FUNCTION(arsort) { zval *array; - long sort_type = PHP_SORT_REGULAR; + zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_data_compare_func(sort_type, 1); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 0 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 0) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -536,15 +937,16 @@ PHP_FUNCTION(arsort) PHP_FUNCTION(sort) { zval *array; - long sort_type = PHP_SORT_REGULAR; + zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_data_compare_func(sort_type, 0); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_data_compare, 1 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; @@ -556,46 +958,48 @@ PHP_FUNCTION(sort) PHP_FUNCTION(rsort) { zval *array; - long sort_type = PHP_SORT_REGULAR; + zend_long sort_type = PHP_SORT_REGULAR; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/|l", &array, &sort_type) == FAILURE) { RETURN_FALSE; } - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_data_compare_func(sort_type, 1); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_reverse_data_compare, 1 TSRMLS_CC) == FAILURE) { + if (zend_hash_sort(Z_ARRVAL_P(array), cmp, 1) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ -static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ +static int php_array_user_compare(const void *a, const void *b) /* {{{ */ { Bucket *f; Bucket *s; - zval **args[2]; - zval *retval_ptr = NULL; + zval args[2]; + zval retval; - f = *((Bucket **) a); - s = *((Bucket **) b); + f = (Bucket *) a; + s = (Bucket *) b; - args[0] = (zval **) f->pData; - args[1] = (zval **) s->pData; + ZVAL_COPY(&args[0], &f->val); + ZVAL_COPY(&args[1], &s->val); BG(user_compare_fci).param_count = 2; BG(user_compare_fci).params = args; - BG(user_compare_fci).retval_ptr_ptr = &retval_ptr; + BG(user_compare_fci).retval = &retval; BG(user_compare_fci).no_separation = 0; - if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) { - long retval; - - convert_to_long_ex(&retval_ptr); - retval = Z_LVAL_P(retval_ptr); - zval_ptr_dtor(&retval_ptr); - return retval < 0 ? -1 : retval > 0 ? 1 : 0; + if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { + zend_long ret = zval_get_long(&retval); + zval_ptr_dtor(&retval); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[0]); + return ret < 0 ? -1 : ret > 0 ? 1 : 0; } else { + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[0]); return 0; } } @@ -603,8 +1007,8 @@ static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ /* check if comparison function is valid */ #define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \ - if (!zend_is_callable(*func_name, 0, NULL TSRMLS_CC)) { \ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid comparison function"); \ + if (!zend_is_callable(*func_name, 0, NULL)) { \ + php_error_docref(NULL, E_WARNING, "Invalid comparison function"); \ BG(user_compare_fci) = old_user_compare_fci; \ BG(user_compare_fci_cache) = old_user_compare_fci_cache; \ RETURN_FALSE; \ @@ -630,46 +1034,51 @@ static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ BG(user_compare_fci) = old_user_compare_fci; \ BG(user_compare_fci_cache) = old_user_compare_fci_cache; \ -/* {{{ proto bool usort(array array_arg, string cmp_function) - Sort an array by values using a user-defined comparison function */ -PHP_FUNCTION(usort) +static void php_usort(INTERNAL_FUNCTION_PARAMETERS, compare_func_t compare_func, zend_bool renumber) /* {{{ */ { zval *array; - unsigned int refcount; + zend_refcounted *arr; + zend_bool retval; PHP_ARRAY_CMP_FUNC_VARS; PHP_ARRAY_CMP_FUNC_BACKUP(); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/f", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { PHP_ARRAY_CMP_FUNC_RESTORE(); return; } - /* Clear the is_ref flag, so the attemts to modify the array in user + /* Increase reference counter, so the attempts to modify the array in user * comparison function will create a copy of array and won't affect the - * original array. The fact of modification is detected using refcount - * comparison. The result of sorting in such case is undefined and the - * function returns FALSE. + * original array. The fact of modification is detected by comparing the + * zend_array pointer. The result of sorting in such case is undefined and + * the function returns FALSE. */ - Z_UNSET_ISREF_P(array); - refcount = Z_REFCOUNT_P(array); + Z_ADDREF_P(array); + arr = Z_COUNTED_P(array); - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 1 TSRMLS_CC) == FAILURE) { - RETVAL_FALSE; - } else { - if (refcount > Z_REFCOUNT_P(array)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function"); - RETVAL_FALSE; - } else { - RETVAL_TRUE; + retval = zend_hash_sort(Z_ARRVAL_P(array), compare_func, renumber) != FAILURE; + + if (arr != Z_COUNTED_P(array)) { + php_error_docref(NULL, E_WARNING, "Array was modified by the user comparison function"); + if (--GC_REFCOUNT(arr) <= 0) { + _zval_dtor_func(arr ZEND_FILE_LINE_CC); } - } - - if (Z_REFCOUNT_P(array) > 1) { - Z_SET_ISREF_P(array); + retval = 0; + } else { + Z_DELREF_P(array); } PHP_ARRAY_CMP_FUNC_RESTORE(); + RETURN_BOOL(retval); +} +/* }}} */ + +/* {{{ proto bool usort(array array_arg, string cmp_function) + Sort an array by values using a user-defined comparison function */ +PHP_FUNCTION(usort) +{ + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1); } /* }}} */ @@ -677,95 +1086,50 @@ PHP_FUNCTION(usort) Sort an array with a user-defined comparison function and maintain index association */ PHP_FUNCTION(uasort) { - zval *array; - unsigned int refcount; - PHP_ARRAY_CMP_FUNC_VARS; - - PHP_ARRAY_CMP_FUNC_BACKUP(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { - PHP_ARRAY_CMP_FUNC_RESTORE(); - return; - } - - /* Clear the is_ref flag, so the attemts to modify the array in user - * comaprison function will create a copy of array and won't affect the - * original array. The fact of modification is detected using refcount - * comparison. The result of sorting in such case is undefined and the - * function returns FALSE. - */ - Z_UNSET_ISREF_P(array); - refcount = Z_REFCOUNT_P(array); - - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_compare, 0 TSRMLS_CC) == FAILURE) { - RETVAL_FALSE; - } else { - if (refcount > Z_REFCOUNT_P(array)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function"); - RETVAL_FALSE; - } else { - RETVAL_TRUE; - } - } - - if (Z_REFCOUNT_P(array) > 1) { - Z_SET_ISREF_P(array); - } - - PHP_ARRAY_CMP_FUNC_RESTORE(); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0); } /* }}} */ -static int php_array_user_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ +static int php_array_user_key_compare(const void *a, const void *b) /* {{{ */ { Bucket *f; Bucket *s; - zval *key1, *key2; - zval **args[2]; - zval *retval_ptr = NULL; - long result; - - ALLOC_INIT_ZVAL(key1); - ALLOC_INIT_ZVAL(key2); - args[0] = &key1; - args[1] = &key2; - - f = *((Bucket **) a); - s = *((Bucket **) b); - - if (f->nKeyLength == 0) { - Z_LVAL_P(key1) = f->h; - Z_TYPE_P(key1) = IS_LONG; + zval args[2]; + zval retval; + zend_long result; + + ZVAL_NULL(&args[0]); + ZVAL_NULL(&args[1]); + + f = (Bucket *) a; + s = (Bucket *) b; + + if (f->key == NULL) { + ZVAL_LONG(&args[0], f->h); } else { - Z_STRVAL_P(key1) = estrndup(f->arKey, f->nKeyLength - 1); - Z_STRLEN_P(key1) = f->nKeyLength - 1; - Z_TYPE_P(key1) = IS_STRING; + ZVAL_STR_COPY(&args[0], f->key); } - if (s->nKeyLength == 0) { - Z_LVAL_P(key2) = s->h; - Z_TYPE_P(key2) = IS_LONG; + if (s->key == NULL) { + ZVAL_LONG(&args[1], s->h); } else { - Z_STRVAL_P(key2) = estrndup(s->arKey, s->nKeyLength - 1); - Z_STRLEN_P(key2) = s->nKeyLength - 1; - Z_TYPE_P(key2) = IS_STRING; + ZVAL_STR_COPY(&args[1], s->key); } BG(user_compare_fci).param_count = 2; BG(user_compare_fci).params = args; - BG(user_compare_fci).retval_ptr_ptr = &retval_ptr; + BG(user_compare_fci).retval = &retval; BG(user_compare_fci).no_separation = 0; - if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) { - convert_to_long_ex(&retval_ptr); - result = Z_LVAL_P(retval_ptr); - zval_ptr_dtor(&retval_ptr); + if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { + result = zval_get_long(&retval); + zval_ptr_dtor(&retval); } else { result = 0; } - zval_ptr_dtor(&key1); - zval_ptr_dtor(&key2); + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&args[1]); - return result; + return result < 0 ? -1 : result > 0 ? 1 : 0; } /* }}} */ @@ -773,42 +1137,7 @@ static int php_array_user_key_compare(const void *a, const void *b TSRMLS_DC) /* Sort an array by keys using a user-defined comparison function */ PHP_FUNCTION(uksort) { - zval *array; - unsigned int refcount; - PHP_ARRAY_CMP_FUNC_VARS; - - PHP_ARRAY_CMP_FUNC_BACKUP(); - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af", &array, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { - PHP_ARRAY_CMP_FUNC_RESTORE(); - return; - } - - /* Clear the is_ref flag, so the attemts to modify the array in user - * comaprison function will create a copy of array and won't affect the - * original array. The fact of modification is detected using refcount - * comparison. The result of sorting in such case is undefined and the - * function returns FALSE. - */ - Z_UNSET_ISREF_P(array); - refcount = Z_REFCOUNT_P(array); - - if (zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_array_user_key_compare, 0 TSRMLS_CC) == FAILURE) { - RETVAL_FALSE; - } else { - if (refcount > Z_REFCOUNT_P(array)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array was modified by the user comparison function"); - RETVAL_FALSE; - } else { - RETVAL_TRUE; - } - } - - if (Z_REFCOUNT_P(array) > 1) { - Z_SET_ISREF_P(array); - } - - PHP_ARRAY_CMP_FUNC_RESTORE(); + php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0); } /* }}} */ @@ -817,20 +1146,31 @@ PHP_FUNCTION(uksort) PHP_FUNCTION(end) { HashTable *array; - zval **entry; + zval *entry; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif zend_hash_internal_pointer_end(array); - if (return_value_used) { - if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) { + if (USED_RET()) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { RETURN_FALSE; } - RETURN_ZVAL_FAST(*entry); + if (Z_TYPE_P(entry) == IS_INDIRECT) { + entry = Z_INDIRECT_P(entry); + } + + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -840,20 +1180,31 @@ PHP_FUNCTION(end) PHP_FUNCTION(prev) { HashTable *array; - zval **entry; + zval *entry; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif zend_hash_move_backwards(array); - if (return_value_used) { - if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) { + if (USED_RET()) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { RETURN_FALSE; } - RETURN_ZVAL_FAST(*entry); + if (Z_TYPE_P(entry) == IS_INDIRECT) { + entry = Z_INDIRECT_P(entry); + } + + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -863,20 +1214,31 @@ PHP_FUNCTION(prev) PHP_FUNCTION(next) { HashTable *array; - zval **entry; + zval *entry; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif zend_hash_move_forward(array); - if (return_value_used) { - if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) { + if (USED_RET()) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { RETURN_FALSE; } - RETURN_ZVAL_FAST(*entry); + if (Z_TYPE_P(entry) == IS_INDIRECT) { + entry = Z_INDIRECT_P(entry); + } + + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -886,20 +1248,31 @@ PHP_FUNCTION(next) PHP_FUNCTION(reset) { HashTable *array; - zval **entry; + zval *entry; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/", &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif zend_hash_internal_pointer_reset(array); - if (return_value_used) { - if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) { + if (USED_RET()) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { RETURN_FALSE; } - RETURN_ZVAL_FAST(*entry); + if (Z_TYPE_P(entry) == IS_INDIRECT) { + entry = Z_INDIRECT_P(entry); + } + + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } } /* }}} */ @@ -909,17 +1282,28 @@ PHP_FUNCTION(reset) PHP_FUNCTION(current) { HashTable *array; - zval **entry; + zval *entry; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H", &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_OR_OBJECT_HT(array) + ZEND_PARSE_PARAMETERS_END(); +#endif - if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { RETURN_FALSE; } - RETURN_ZVAL_FAST(*entry); + if (Z_TYPE_P(entry) == IS_INDIRECT) { + entry = Z_INDIRECT_P(entry); + } + + ZVAL_DEREF(entry); + ZVAL_COPY(return_value, entry); } /* }}} */ @@ -929,9 +1313,15 @@ PHP_FUNCTION(key) { HashTable *array; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H", &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_OR_OBJECT_HT(array) + ZEND_PARSE_PARAMETERS_END(); +#endif zend_hash_get_current_key_zval(array, return_value); } @@ -942,48 +1332,44 @@ PHP_FUNCTION(key) PHP_FUNCTION(min) { int argc; - zval ***args = NULL; + zval *args = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { return; } - - php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC); - + /* mixed min ( array $values ) */ if (argc == 1) { - zval **result; - - if (Z_TYPE_PP(args[0]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array"); + zval *result; + + if (Z_TYPE(args[0]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "When only one parameter is given, it must be an array"); RETVAL_NULL(); } else { - if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 0, (void **) &result TSRMLS_CC) == SUCCESS) { - RETVAL_ZVAL_FAST(*result); + if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) { + ZVAL_DEREF(result); + ZVAL_COPY(return_value, result); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element"); + php_error_docref(NULL, E_WARNING, "Array must contain at least one element"); RETVAL_FALSE; } } } else { /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */ - zval **min, result; + zval *min, result; int i; - min = args[0]; + min = &args[0]; for (i = 1; i < argc; i++) { - is_smaller_function(&result, *args[i], *min TSRMLS_CC); - if (Z_LVAL(result) == 1) { - min = args[i]; + is_smaller_function(&result, &args[i], min); + if (Z_TYPE(result) == IS_TRUE) { + min = &args[i]; } } - RETVAL_ZVAL_FAST(*min); - } - - if (args) { - efree(args); + ZVAL_DEREF(min); + ZVAL_COPY(return_value, min); } } /* }}} */ @@ -992,85 +1378,90 @@ PHP_FUNCTION(min) Return the highest value in an array or a series of arguments */ PHP_FUNCTION(max) { - zval ***args = NULL; + zval *args = NULL; int argc; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) { + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { return; } - php_set_compare_func(PHP_SORT_REGULAR TSRMLS_CC); - /* mixed max ( array $values ) */ if (argc == 1) { - zval **result; + zval *result; - if (Z_TYPE_PP(args[0]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "When only one parameter is given, it must be an array"); + if (Z_TYPE(args[0]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "When only one parameter is given, it must be an array"); RETVAL_NULL(); } else { - if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 1, (void **) &result TSRMLS_CC) == SUCCESS) { - RETVAL_ZVAL_FAST(*result); + if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) { + ZVAL_DEREF(result); + ZVAL_COPY(return_value, result); } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element"); + php_error_docref(NULL, E_WARNING, "Array must contain at least one element"); RETVAL_FALSE; } } } else { /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */ - zval **max, result; + zval *max, result; int i; - max = args[0]; + max = &args[0]; for (i = 1; i < argc; i++) { - is_smaller_or_equal_function(&result, *args[i], *max TSRMLS_CC); - if (Z_LVAL(result) == 0) { - max = args[i]; + is_smaller_or_equal_function(&result, &args[i], max); + if (Z_TYPE(result) == IS_FALSE) { + max = &args[i]; } } - RETVAL_ZVAL_FAST(*max); - } - - if (args) { - efree(args); + ZVAL_DEREF(max); + ZVAL_COPY(return_value, max); } } /* }}} */ -static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive TSRMLS_DC) /* {{{ */ +static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive) /* {{{ */ { - zval **args[3], /* Arguments to userland function */ - *retval_ptr = NULL, /* Return value - unused */ - *key=NULL; /* Entry key */ + zval args[3], /* Arguments to userland function */ + retval, /* Return value - unused */ + *zv; /* Set up known arguments */ - args[1] = &key; - args[2] = &userdata; + ZVAL_UNDEF(&args[1]); if (userdata) { - Z_ADDREF_P(userdata); + ZVAL_COPY(&args[2], userdata); } - BG(array_walk_fci).retval_ptr_ptr = &retval_ptr; + BG(array_walk_fci).retval = &retval; BG(array_walk_fci).param_count = userdata ? 3 : 2; BG(array_walk_fci).params = args; BG(array_walk_fci).no_separation = 0; - + /* Iterate through hash */ zend_hash_internal_pointer_reset(target_hash); - while (!EG(exception) && zend_hash_get_current_data(target_hash, (void **)&args[0]) == SUCCESS) { - if (recursive && Z_TYPE_PP(args[0]) == IS_ARRAY) { + while (!EG(exception) && (zv = zend_hash_get_current_data(target_hash)) != NULL) { + if (Z_TYPE_P(zv) == IS_INDIRECT) { + zv = Z_INDIRECT_P(zv); + if (Z_TYPE_P(zv) == IS_UNDEF) { + zend_hash_move_forward(target_hash); + continue; + } + } + if (recursive && + (Z_TYPE_P(zv) == IS_ARRAY || + (Z_ISREF_P(zv) && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY))) { HashTable *thash; zend_fcall_info orig_array_walk_fci; zend_fcall_info_cache orig_array_walk_fci_cache; - SEPARATE_ZVAL_IF_NOT_REF(args[0]); - thash = Z_ARRVAL_PP(args[0]); - if (thash->nApplyCount > 1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); + ZVAL_DEREF(zv); + SEPARATE_ARRAY(zv); + thash = Z_ARRVAL_P(zv); + if (thash->u.v.nApplyCount > 1) { + php_error_docref(NULL, E_WARNING, "recursion detected"); if (userdata) { - zval_ptr_dtor(&userdata); + zval_ptr_dtor(&args[2]); } return 0; } @@ -1079,41 +1470,53 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive orig_array_walk_fci = BG(array_walk_fci); orig_array_walk_fci_cache = BG(array_walk_fci_cache); - thash->nApplyCount++; - php_array_walk(thash, userdata, recursive TSRMLS_CC); - thash->nApplyCount--; + thash->u.v.nApplyCount++; + php_array_walk(thash, userdata, recursive); + thash->u.v.nApplyCount--; /* restore the fcall info and cache */ BG(array_walk_fci) = orig_array_walk_fci; BG(array_walk_fci_cache) = orig_array_walk_fci_cache; } else { + int was_ref = Z_ISREF_P(zv); + + ZVAL_COPY(&args[0], zv); + /* Allocate space for key */ - MAKE_STD_ZVAL(key); - zend_hash_get_current_key_zval(target_hash, key); + zend_hash_get_current_key_zval(target_hash, &args[1]); /* Call the userland function */ - if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache) TSRMLS_CC) == SUCCESS) { - if (retval_ptr) { - zval_ptr_dtor(&retval_ptr); + if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache)) == SUCCESS) { + if (!was_ref && Z_ISREF(args[0])) { + /* copy reference back */ + zval garbage; + + ZVAL_COPY_VALUE(&garbage, zv); + ZVAL_COPY_VALUE(zv, &args[0]); + zval_ptr_dtor(&garbage); + } else { + zval_ptr_dtor(&args[0]); } + zval_ptr_dtor(&retval); } else { - if (key) { - zval_ptr_dtor(&key); - key = NULL; + zval_ptr_dtor(&args[0]); + if (Z_TYPE(args[1]) != IS_UNDEF) { + zval_ptr_dtor(&args[1]); + ZVAL_UNDEF(&args[1]); } break; } } - if (key) { - zval_ptr_dtor(&key); - key = NULL; + if (Z_TYPE(args[1]) != IS_UNDEF) { + zval_ptr_dtor(&args[1]); + ZVAL_UNDEF(&args[1]); } zend_hash_move_forward(target_hash); } if (userdata) { - zval_ptr_dtor(&userdata); + zval_ptr_dtor(&args[2]); } return 0; } @@ -1131,13 +1534,26 @@ PHP_FUNCTION(array_walk) orig_array_walk_fci = BG(array_walk_fci); orig_array_walk_fci_cache = BG(array_walk_fci_cache); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) { BG(array_walk_fci) = orig_array_walk_fci; BG(array_walk_fci_cache) = orig_array_walk_fci_cache; return; } +#else + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1) + Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache)) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_EX(userdata, 0, 1) + ZEND_PARSE_PARAMETERS_END_EX( + BG(array_walk_fci) = orig_array_walk_fci; + BG(array_walk_fci_cache) = orig_array_walk_fci_cache; + return + ); +#endif - php_array_walk(array, userdata, 0 TSRMLS_CC); + php_array_walk(array, userdata, 0); BG(array_walk_fci) = orig_array_walk_fci; BG(array_walk_fci_cache) = orig_array_walk_fci_cache; RETURN_TRUE; @@ -1156,13 +1572,13 @@ PHP_FUNCTION(array_walk_recursive) orig_array_walk_fci = BG(array_walk_fci); orig_array_walk_fci_cache = BG(array_walk_fci_cache); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hf|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "H/f|z/", &array, &BG(array_walk_fci), &BG(array_walk_fci_cache), &userdata) == FAILURE) { BG(array_walk_fci) = orig_array_walk_fci; BG(array_walk_fci_cache) = orig_array_walk_fci_cache; return; } - php_array_walk(array, userdata, 1 TSRMLS_CC); + php_array_walk(array, userdata, 1); BG(array_walk_fci) = orig_array_walk_fci; BG(array_walk_fci_cache) = orig_array_walk_fci_cache; RETURN_TRUE; @@ -1173,36 +1589,92 @@ PHP_FUNCTION(array_walk_recursive) * 0 = return boolean * 1 = return key */ -static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */ +static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */ { zval *value, /* value to check for */ *array, /* array to check in */ - **entry, /* pointer to array entry */ - res; /* comparison result */ - HashPosition pos; /* hash iterator */ + *entry; /* pointer to array entry */ + zend_ulong num_idx; + zend_string *str_idx; zend_bool strict = 0; /* strict comparison or not */ - int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "za|b", &value, &array, &strict) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_ZVAL(value) + Z_PARAM_ARRAY(array) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(strict) + ZEND_PARSE_PARAMETERS_END(); +#endif if (strict) { - is_equal_func = is_identical_function; - } - - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) { - is_equal_func(&res, value, *entry TSRMLS_CC); - if (Z_LVAL(res)) { - if (behavior == 0) { - RETURN_TRUE; - } else { - zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), return_value, &pos); - return; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { + ZVAL_DEREF(entry); + if (fast_is_identical_function(value, entry)) { + if (behavior == 0) { + RETURN_TRUE; + } else { + if (str_idx) { + RETVAL_STR_COPY(str_idx); + } else { + RETVAL_LONG(num_idx); + } + return; + } } - } - zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos); + } ZEND_HASH_FOREACH_END(); + } else { + ZVAL_DEREF(value); + if (Z_TYPE_P(value) == IS_LONG) { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { + if (fast_equal_check_long(value, entry)) { + if (behavior == 0) { + RETURN_TRUE; + } else { + if (str_idx) { + RETVAL_STR_COPY(str_idx); + } else { + RETVAL_LONG(num_idx); + } + return; + } + } + } ZEND_HASH_FOREACH_END(); + } else if (Z_TYPE_P(value) == IS_STRING) { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { + if (fast_equal_check_string(value, entry)) { + if (behavior == 0) { + RETURN_TRUE; + } else { + if (str_idx) { + RETVAL_STR_COPY(str_idx); + } else { + RETVAL_LONG(num_idx); + } + return; + } + } + } ZEND_HASH_FOREACH_END(); + } else { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { + if (fast_equal_check_function(value, entry)) { + if (behavior == 0) { + RETURN_TRUE; + } else { + if (str_idx) { + RETVAL_STR_COPY(str_idx); + } else { + RETVAL_LONG(num_idx); + } + return; + } + } + } ZEND_HASH_FOREACH_END(); + } } RETURN_FALSE; @@ -1225,47 +1697,66 @@ PHP_FUNCTION(array_search) } /* }}} */ -static int php_valid_var_name(char *var_name, int var_name_len) /* {{{ */ +static zend_always_inline int php_valid_var_name(char *var_name, size_t var_name_len) /* {{{ */ { - int i, ch; +#if 1 + /* first 256 bits for first character, and second 256 bits for the next */ + static const uint32_t charset[16] = { + /* 31 0 63 32 95 64 127 96 */ + 0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + /* 31 0 63 32 95 64 127 96 */ + 0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff + }; +#endif + size_t i; + uint32_t ch; - if (!var_name || !var_name_len) { + if (UNEXPECTED(!var_name_len)) { return 0; } - + /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */ - ch = (int)((unsigned char *)var_name)[0]; + ch = (uint32_t)((unsigned char *)var_name)[0]; +#if 1 + if (UNEXPECTED(!(charset[ch >> 5] & (1 << (ch & 0x1f))))) { +#else if (var_name[0] != '_' && (ch < 65 /* A */ || /* Z */ ch > 90) && (ch < 97 /* a */ || /* z */ ch > 122) && (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255) ) { +#endif return 0; } /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */ if (var_name_len > 1) { - for (i = 1; i < var_name_len; i++) { - ch = (int)((unsigned char *)var_name)[i]; + i = 1; + do { + ch = (uint32_t)((unsigned char *)var_name)[i]; +#if 1 + if (UNEXPECTED(!(charset[8 + (ch >> 5)] & (1 << (ch & 0x1f))))) { +#else if (var_name[i] != '_' && (ch < 48 /* 0 */ || /* 9 */ ch > 57) && (ch < 65 /* A */ || /* Z */ ch > 90) && (ch < 97 /* a */ || /* z */ ch > 122) && (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255) ) { +#endif return 0; } - } + } while (++i < var_name_len); } return 1; } /* }}} */ -PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, int var_name_len, zend_bool add_underscore TSRMLS_DC) /* {{{ */ +PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, size_t var_name_len, zend_bool add_underscore) /* {{{ */ { - Z_STRLEN_P(result) = Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len; - Z_TYPE_P(result) = IS_STRING; - Z_STRVAL_P(result) = emalloc(Z_STRLEN_P(result) + 1); + ZVAL_NEW_STR(result, zend_string_alloc(Z_STRLEN_P(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0)); memcpy(Z_STRVAL_P(result), Z_STRVAL_P(prefix), Z_STRLEN_P(prefix)); if (add_underscore) { @@ -1283,72 +1774,72 @@ PHPAPI int php_prefix_varname(zval *result, zval *prefix, char *var_name, int va PHP_FUNCTION(extract) { zval *var_array, *prefix = NULL; - long extract_type = EXTR_OVERWRITE; - zval **entry, *data; - char *var_name; - ulong num_key; - uint var_name_len; - int var_exists, key_type, count = 0; + zend_long extract_type = EXTR_OVERWRITE; + zval *entry; + zend_string *var_name; + zend_ulong num_key; + int var_exists, count = 0; int extract_refs = 0; - HashPosition pos; + zend_array *symbol_table; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|lz/", &var_array, &extract_type, &prefix) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ARRAY(var_array) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(extract_type) + Z_PARAM_ZVAL_EX(prefix, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif extract_refs = (extract_type & EXTR_REFS); + if (extract_refs) { + SEPARATE_ZVAL(var_array); + } extract_type &= 0xff; if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid extract type"); + php_error_docref(NULL, E_WARNING, "Invalid extract type"); return; } if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "specified extract type requires the prefix parameter"); + php_error_docref(NULL, E_WARNING, "specified extract type requires the prefix parameter"); return; } if (prefix) { convert_to_string(prefix); if (Z_STRLEN_P(prefix) && !php_valid_var_name(Z_STRVAL_P(prefix), Z_STRLEN_P(prefix))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "prefix is not a valid identifier"); + php_error_docref(NULL, E_WARNING, "prefix is not a valid identifier"); return; } } - if (!EG(active_symbol_table)) { - zend_rebuild_symbol_table(TSRMLS_C); - } - - /* var_array is passed by ref for the needs of EXTR_REFS (needs to - * work on the original array to create refs to its members) - * simulate pass_by_value if EXTR_REFS is not used */ - if (!extract_refs) { - SEPARATE_ARG_IF_REF(var_array); + symbol_table = zend_rebuild_symbol_table(); +#if 0 + if (!symbol_table) { + php_error_docref(NULL, E_WARNING, "failed to build symbol table"); + return; } +#endif - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(var_array), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(var_array), (void **)&entry, &pos) == SUCCESS) { + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(var_array), num_key, var_name, entry) { zval final_name; ZVAL_NULL(&final_name); - - key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(var_array), &var_name, &var_name_len, &num_key, 0, &pos); var_exists = 0; - if (key_type == HASH_KEY_IS_STRING) { - var_name_len--; - var_exists = zend_hash_exists(EG(active_symbol_table), var_name, var_name_len + 1); - } else if (key_type == HASH_KEY_IS_LONG && (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID)) { - zval num; - - ZVAL_LONG(&num, num_key); - convert_to_string(&num); - php_prefix_varname(&final_name, prefix, Z_STRVAL(num), Z_STRLEN(num), 1 TSRMLS_CC); - zval_dtor(&num); + if (var_name) { + var_exists = zend_hash_exists_ind(symbol_table, var_name); + } else if (extract_type == EXTR_PREFIX_ALL || extract_type == EXTR_PREFIX_INVALID) { + zend_string *str = zend_long_to_str(num_key); + php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1); + zend_string_release(str); } else { - zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos); continue; } @@ -1359,116 +1850,114 @@ PHP_FUNCTION(extract) case EXTR_OVERWRITE: /* GLOBALS protection */ - if (var_exists && var_name_len == sizeof("GLOBALS")-1 && !strcmp(var_name, "GLOBALS")) { + if (var_exists && ZSTR_LEN(var_name) == sizeof("GLOBALS")-1 && !strcmp(ZSTR_VAL(var_name), "GLOBALS")) { break; } - if (var_exists && var_name_len == sizeof("this")-1 && !strcmp(var_name, "this") && EG(scope) && EG(scope)->name_length != 0) { + if (var_exists && ZSTR_LEN(var_name) == sizeof("this")-1 && !strcmp(ZSTR_VAL(var_name), "this") && EG(scope) && ZSTR_LEN(EG(scope)->name) != 0) { break; } - ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + ZVAL_STR_COPY(&final_name, var_name); break; case EXTR_PREFIX_IF_EXISTS: if (var_exists) { - php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC); + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); } break; case EXTR_PREFIX_SAME: - if (!var_exists && var_name_len != 0) { - ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + if (!var_exists && ZSTR_LEN(var_name) != 0) { + ZVAL_STR_COPY(&final_name, var_name); } /* break omitted intentionally */ case EXTR_PREFIX_ALL: - if (Z_TYPE(final_name) == IS_NULL && var_name_len != 0) { - php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC); + if (Z_TYPE(final_name) == IS_NULL && ZSTR_LEN(var_name) != 0) { + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); } break; case EXTR_PREFIX_INVALID: if (Z_TYPE(final_name) == IS_NULL) { - if (!php_valid_var_name(var_name, var_name_len)) { - php_prefix_varname(&final_name, prefix, var_name, var_name_len, 1 TSRMLS_CC); + if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) { + php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1); } else { - ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + ZVAL_STR_COPY(&final_name, var_name); } } break; default: if (!var_exists) { - ZVAL_STRINGL(&final_name, var_name, var_name_len, 1); + ZVAL_STR_COPY(&final_name, var_name); } break; } - if (Z_TYPE(final_name) != IS_NULL && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + if (Z_TYPE(final_name) == IS_STRING && php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) { + zval *orig_var; if (extract_refs) { - zval **orig_var; - SEPARATE_ZVAL_TO_MAKE_IS_REF(entry); - zval_add_ref(entry); + ZVAL_MAKE_REF(entry); + Z_ADDREF_P(entry); - if (zend_hash_find(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) &orig_var) == SUCCESS) { + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } zval_ptr_dtor(orig_var); - *orig_var = *entry; + ZVAL_COPY_VALUE(orig_var, entry); } else { - zend_hash_update(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, (void **) entry, sizeof(zval *), NULL); + zend_hash_update(symbol_table, Z_STR(final_name), entry); } } else { - MAKE_STD_ZVAL(data); - *data = **entry; - zval_copy_ctor(data); - - ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), Z_STRVAL(final_name), Z_STRLEN(final_name) + 1, data, 1, 0); + ZVAL_DEREF(entry); + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) { + if (Z_TYPE_P(orig_var) == IS_INDIRECT) { + orig_var = Z_INDIRECT_P(orig_var); + } + ZVAL_DEREF(orig_var); + zval_ptr_dtor(orig_var); + ZVAL_COPY_VALUE(orig_var, entry); + } else { + zend_hash_update(symbol_table, Z_STR(final_name), entry); + } } count++; } zval_dtor(&final_name); - - zend_hash_move_forward_ex(Z_ARRVAL_P(var_array), &pos); - } - - if (!extract_refs) { - zval_ptr_dtor(&var_array); - } + } ZEND_HASH_FOREACH_END(); RETURN_LONG(count); } /* }}} */ -static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry TSRMLS_DC) /* {{{ */ +static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry) /* {{{ */ { - zval **value_ptr, *value, *data; + zval *value_ptr, data; + ZVAL_DEREF(entry); if (Z_TYPE_P(entry) == IS_STRING) { - if (zend_hash_find(eg_active_symbol_table, Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, (void **)&value_ptr) != FAILURE) { - value = *value_ptr; - ALLOC_ZVAL(data); - MAKE_COPY_ZVAL(&value, data); - - zend_hash_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(entry), Z_STRLEN_P(entry) + 1, &data, sizeof(zval *), NULL); + if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) { + ZVAL_COPY(&data, value_ptr); + zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data); } - } - else if (Z_TYPE_P(entry) == IS_ARRAY) { - HashPosition pos; - - if ((Z_ARRVAL_P(entry)->nApplyCount > 1)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); + } else if (Z_TYPE_P(entry) == IS_ARRAY) { + if ((Z_ARRVAL_P(entry)->u.v.nApplyCount > 1)) { + php_error_docref(NULL, E_WARNING, "recursion detected"); return; } - Z_ARRVAL_P(entry)->nApplyCount++; - - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(entry), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(entry), (void**)&value_ptr, &pos) == SUCCESS) { - value = *value_ptr; - - php_compact_var(eg_active_symbol_table, return_value, value TSRMLS_CC); - zend_hash_move_forward_ex(Z_ARRVAL_P(entry), &pos); + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) { + Z_ARRVAL_P(entry)->u.v.nApplyCount++; + } + ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) { + php_compact_var(eg_active_symbol_table, return_value, value_ptr); + } ZEND_HASH_FOREACH_END(); + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) { + Z_ARRVAL_P(entry)->u.v.nApplyCount--; } - Z_ARRVAL_P(entry)->nApplyCount--; } } /* }}} */ @@ -1477,32 +1966,31 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu Creates a hash containing variables and their values */ PHP_FUNCTION(compact) { - zval ***args = NULL; /* function arguments array */ - int num_args, i; + zval *args = NULL; /* function arguments array */ + uint32_t num_args, i; + zend_array *symbol_table; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &num_args) == FAILURE) { return; } - if (!EG(active_symbol_table)) { - zend_rebuild_symbol_table(TSRMLS_C); + symbol_table = zend_rebuild_symbol_table(); + + if (UNEXPECTED(symbol_table == NULL)) { + return; } /* compact() is probably most used with a single array of var_names or multiple string names, rather than a combination of both. So quickly guess a minimum result size based on that */ - if (ZEND_NUM_ARGS() == 1 && Z_TYPE_PP(args[0]) == IS_ARRAY) { - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_PP(args[0]))); + if (ZEND_NUM_ARGS() == 1 && Z_TYPE(args[0]) == IS_ARRAY) { + array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0]))); } else { array_init_size(return_value, ZEND_NUM_ARGS()); } for (i=0; i<ZEND_NUM_ARGS(); i++) { - php_compact_var(EG(active_symbol_table), return_value, *args[i] TSRMLS_CC); - } - - if (args) { - efree(args); + php_compact_var(symbol_table, return_value, &args[i]); } } /* }}} */ @@ -1512,34 +2000,34 @@ PHP_FUNCTION(compact) PHP_FUNCTION(array_fill) { zval *val; - long start_key, num; + zend_long start_key, num; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "llz", &start_key, &num, &val) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "llz", &start_key, &num, &val) == FAILURE) { return; } if (num < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements can't be negative"); + php_error_docref(NULL, E_WARNING, "Number of elements can't be negative"); RETURN_FALSE; } /* allocate an array for return */ - array_init_size(return_value, num); + array_init_size(return_value, (uint32_t)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); + zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, val); + Z_TRY_ADDREF_P(val); while (num--) { - if (zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &val, sizeof(zval *), NULL) == SUCCESS) { - zval_add_ref(&val); + if (zend_hash_next_index_insert(Z_ARRVAL_P(return_value), val) != NULL) { + Z_TRY_ADDREF_P(val); } else { zval_dtor(return_value); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied"); + php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied"); RETURN_FALSE; } } @@ -1550,54 +2038,48 @@ PHP_FUNCTION(array_fill) Create an array using the elements of the first parameter as keys each initialized to val */ PHP_FUNCTION(array_fill_keys) { - zval *keys, *val, **entry; - HashPosition pos; + zval *keys, *val, *entry; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az", &keys, &val) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "az", &keys, &val) == FAILURE) { return; } /* Initialize return array */ array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys))); - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry, &pos) == SUCCESS) { - - if (Z_TYPE_PP(entry) == IS_LONG) { - zval_add_ref(&val); - zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &val, sizeof(zval *), NULL); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) { + ZVAL_DEREF(entry); + Z_TRY_ADDREF_P(val); + if (Z_TYPE_P(entry) == IS_LONG) { + zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val); } else { - zval key, *key_ptr = *entry; - - if (Z_TYPE_PP(entry) != IS_STRING) { - key = **entry; - zval_copy_ctor(&key); - convert_to_string(&key); - key_ptr = &key; - } - - zval_add_ref(&val); - zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, &val, sizeof(zval *), NULL); - - if (key_ptr != *entry) { - zval_dtor(&key); - } + zend_string *key = zval_get_string(entry); + zend_symtable_update(Z_ARRVAL_P(return_value), key, val); + zend_string_release(key); } - - zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos); - } + } ZEND_HASH_FOREACH_END(); } /* }}} */ +#define RANGE_CHECK_INIT_ARRAY(start, end) do { \ + double __calc_size = ((start - end) / step) + 1; \ + if (fabs(__calc_size) >= (double)HT_MAX_SIZE) { \ + php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", start > end ? end : start, start > end ? start : end); \ + RETURN_FALSE; \ + } \ + array_init_size(return_value, (uint32_t)fabs(__calc_size)); \ + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \ + } while (0) + /* {{{ proto array range(mixed low, mixed high[, int step]) Create an array containing the range of integers or characters from low to high (inclusive) */ PHP_FUNCTION(range) { - zval *zlow, *zhigh, *zstep = NULL; + zval *zlow, *zhigh, *zstep = NULL, tmp; int err = 0, is_step_double = 0; double step = 1.0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/|z/", &zlow, &zhigh, &zstep) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &zlow, &zhigh, &zstep) == FAILURE) { RETURN_FALSE; } @@ -1608,8 +2090,7 @@ PHP_FUNCTION(range) is_step_double = 1; } - convert_to_double_ex(&zstep); - step = Z_DVAL_P(zstep); + step = zval_get_double(zstep); /* We only want positive step values. */ if (step < 0.0) { @@ -1617,14 +2098,11 @@ PHP_FUNCTION(range) } } - /* Initialize the return_value as an array. */ - array_init(return_value); - /* If the range is given as strings, generate an array of characters. */ if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) { int type1, type2; - unsigned char *low, *high; - long lstep = (long) step; + unsigned char low, high; + zend_long lstep = (zend_long) step; type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0); type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0); @@ -1635,116 +2113,162 @@ PHP_FUNCTION(range) goto long_str; } - convert_to_string(zlow); - convert_to_string(zhigh); - low = (unsigned char *)Z_STRVAL_P(zlow); - high = (unsigned char *)Z_STRVAL_P(zhigh); - - if (*low > *high) { /* Negative Steps */ - unsigned char ch = *low; + low = (unsigned char)Z_STRVAL_P(zlow)[0]; + high = (unsigned char)Z_STRVAL_P(zhigh)[0]; + if (low > high) { /* Negative Steps */ if (lstep <= 0) { err = 1; goto err; } - for (; ch >= *high; ch -= (unsigned int)lstep) { - add_next_index_stringl(return_value, (const char *)&ch, 1, 1); - if (((signed int)ch - lstep) < 0) { - break; + /* Initialize the return_value as an array. */ + array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1)); + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + for (; low >= high; low -= (unsigned int)lstep) { + if (CG(one_char_string)[low]) { + ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]); + } else { + ZVAL_STRINGL(&tmp, (char*)&low, 1); + } + ZEND_HASH_FILL_ADD(&tmp); + if (((signed int)low - lstep) < 0) { + break; + } } - } - } else if (*high > *low) { /* Positive Steps */ - unsigned char ch = *low; - + } ZEND_HASH_FILL_END(); + } else if (high > low) { /* Positive Steps */ if (lstep <= 0) { err = 1; goto err; } - for (; ch <= *high; ch += (unsigned int)lstep) { - add_next_index_stringl(return_value, (const char *)&ch, 1, 1); - if (((signed int)ch + lstep) > 255) { - break; + array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1)); + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + for (; low <= high; low += (unsigned int)lstep) { + if (CG(one_char_string)[low]) { + ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]); + } else { + ZVAL_STRINGL(&tmp, (char*)&low, 1); + } + ZEND_HASH_FILL_ADD(&tmp); + if (((signed int)low + lstep) > 255) { + break; + } } - } + } ZEND_HASH_FILL_END(); } else { - add_next_index_stringl(return_value, (const char *)low, 1, 1); + array_init(return_value); + if (CG(one_char_string)[low]) { + ZVAL_INTERNED_STR(&tmp, CG(one_char_string)[low]); + } else { + ZVAL_STRINGL(&tmp, (char*)&low, 1); + } + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); } - } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) { double low, high, value; - long i; + zend_long i; double_str: - convert_to_double(zlow); - convert_to_double(zhigh); - low = Z_DVAL_P(zlow); - high = Z_DVAL_P(zhigh); + low = zval_get_double(zlow); + high = zval_get_double(zhigh); i = 0; + if (zend_isinf(high) || zend_isinf(low)) { + php_error_docref(NULL, E_WARNING, "Invalid range supplied: start=%0.0f end=%0.0f", low, high); + RETURN_FALSE; + } + + Z_TYPE_INFO(tmp) = IS_DOUBLE; if (low > high) { /* Negative steps */ if (low - high < step || step <= 0) { err = 1; goto err; } + RANGE_CHECK_INIT_ARRAY(low, high); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) { - add_next_index_double(return_value, value); + Z_DVAL(tmp) = value; + ZEND_HASH_FILL_ADD(&tmp); } + } ZEND_HASH_FILL_END(); } else if (high > low) { /* Positive steps */ if (high - low < step || step <= 0) { err = 1; goto err; } - for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) { - add_next_index_double(return_value, value); - } + RANGE_CHECK_INIT_ARRAY(high, low); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) { + Z_DVAL(tmp) = value; + ZEND_HASH_FILL_ADD(&tmp); + } + } ZEND_HASH_FILL_END(); } else { - add_next_index_double(return_value, low); + array_init(return_value); + Z_DVAL(tmp) = low; + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); } } else { double low, high; - long lstep; + zend_long lstep; long_str: - convert_to_double(zlow); - convert_to_double(zhigh); - low = Z_DVAL_P(zlow); - high = Z_DVAL_P(zhigh); - lstep = (long) step; + low = zval_get_double(zlow); + high = zval_get_double(zhigh); + lstep = (zend_long) step; + Z_TYPE_INFO(tmp) = IS_LONG; if (low > high) { /* Negative steps */ if (low - high < lstep || lstep <= 0) { err = 1; goto err; } - for (; low >= high; low -= lstep) { - add_next_index_long(return_value, (long)low); - } + + RANGE_CHECK_INIT_ARRAY(low, high); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + for (; low >= high; low -= lstep) { + Z_LVAL(tmp) = (zend_long)low; + ZEND_HASH_FILL_ADD(&tmp); + } + } ZEND_HASH_FILL_END(); } else if (high > low) { /* Positive steps */ if (high - low < lstep || lstep <= 0) { err = 1; goto err; } - for (; low <= high; low += lstep) { - add_next_index_long(return_value, (long)low); - } + + RANGE_CHECK_INIT_ARRAY(high, low); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + for (; low <= high; low += lstep) { + Z_LVAL(tmp) = (zend_long)low; + ZEND_HASH_FILL_ADD(&tmp); + } + } ZEND_HASH_FILL_END(); } else { - add_next_index_long(return_value, (long)low); + array_init(return_value); + Z_LVAL(tmp) = (zend_long)low; + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp); } } err: if (err) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "step exceeds the specified range"); - zval_dtor(return_value); + php_error_docref(NULL, E_WARNING, "step exceeds the specified range"); RETURN_FALSE; } } /* }}} */ -static void php_array_data_shuffle(zval *array TSRMLS_DC) /* {{{ */ +#undef RANGE_CHECK_INIT_ARRAY + +static void php_array_data_shuffle(zval *array) /* {{{ */ { - Bucket **elems, *temp; + uint32_t idx, j, n_elems; + Bucket *p, temp; HashTable *hash; - int j, n_elems, rnd_idx, n_left; + zend_long rnd_idx; + uint32_t n_left; n_elems = zend_hash_num_elements(Z_ARRVAL_P(array)); @@ -1752,41 +2276,74 @@ static void php_array_data_shuffle(zval *array TSRMLS_DC) /* {{{ */ return; } - elems = (Bucket **)safe_emalloc(n_elems, sizeof(Bucket *), 0); hash = Z_ARRVAL_P(array); n_left = n_elems; - for (j = 0, temp = hash->pListHead; temp; temp = temp->pListNext) - elems[j++] = temp; - while (--n_left) { - rnd_idx = php_rand(TSRMLS_C); - RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); - if (rnd_idx != n_left) { - temp = elems[n_left]; - elems[n_left] = elems[rnd_idx]; - elems[rnd_idx] = temp; + if (EXPECTED(hash->u.v.nIteratorsCount == 0)) { + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + } + j++; + } + } + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + } + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0); + + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + if (idx == iter_pos) { + zend_hash_iterators_update(hash, idx, j); + iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1); + } + } + j++; + } + } + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left); + } } } - HANDLE_BLOCK_INTERRUPTIONS(); - hash->pListHead = elems[0]; - hash->pListTail = NULL; - hash->pInternalPointer = hash->pListHead; + hash->nNumUsed = n_elems; + hash->nInternalPointer = 0; for (j = 0; j < n_elems; j++) { - if (hash->pListTail) { - hash->pListTail->pListNext = elems[j]; + p = hash->arData + j; + if (p->key) { + zend_string_release(p->key); } - elems[j]->pListLast = hash->pListTail; - elems[j]->pListNext = NULL; - hash->pListTail = elems[j]; + p->h = j; + p->key = NULL; + } + hash->nNextFreeElement = n_elems; + if (!(hash->u.flags & HASH_FLAG_PACKED)) { + zend_hash_to_packed(hash); } - temp = hash->pListHead; - j = 0; - zend_hash_reindex(hash, 0); HANDLE_UNBLOCK_INTERRUPTIONS(); - - efree(elems); } /* }}} */ @@ -1796,25 +2353,153 @@ PHP_FUNCTION(shuffle) { zval *array; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &array) == FAILURE) { RETURN_FALSE; } - php_array_data_shuffle(array TSRMLS_CC); + php_array_data_shuffle(array); RETURN_TRUE; } /* }}} */ -PHPAPI void php_splice(HashTable *ht, zend_uint offset, zend_uint length, zval ***list, zend_uint list_count, HashTable *removed TSRMLS_DC) /* {{{ */ +static void php_splice(HashTable *in_hash, int offset, int length, HashTable *replace, HashTable *removed) /* {{{ */ { - zend_hash_splice(ht, sizeof(zval *), (copy_ctor_func_t) zval_add_ref, offset, length, (void **) list, list_count, removed); + HashTable out_hash; /* Output hashtable */ + int num_in, /* Number of entries in the input hashtable */ + pos; /* Current position in the hashtable */ + uint idx; + Bucket *p; /* Pointer to hash bucket */ + zval *entry; /* Hash entry */ + uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0); - zend_hash_internal_pointer_reset(ht); + /* Get number of entries in the input hash */ + num_in = zend_hash_num_elements(in_hash); - if (ht == &EG(symbol_table)) { - zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC); + /* 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 */ + zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), 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, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) { + p = in_hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + /* Get entry and increase reference count */ + entry = &p->val; + + /* Update output hash depending on key type */ + if (p->key == NULL) { + zend_hash_next_index_insert_new(&out_hash, entry); + } else { + zend_hash_add_new(&out_hash, p->key, entry); + } + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); + } + pos++; } + + /* If hash for removed entries exists, go until offset+length and copy the entries to it */ + if (removed != NULL) { + for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) { + p = in_hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + pos++; + entry = &p->val; + if (Z_REFCOUNTED_P(entry)) { + Z_ADDREF_P(entry); + } + if (p->key == NULL) { + zend_hash_next_index_insert_new(removed, entry); + zend_hash_index_del(in_hash, p->h); + } else { + zend_hash_add_new(removed, p->key, entry); + if (in_hash == &EG(symbol_table)) { + zend_delete_global_variable(p->key); + } else { + zend_hash_del(in_hash, p->key); + } + } + } + } else { /* otherwise just skip those entries */ + int pos2 = pos; + + for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) { + p = in_hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + pos2++; + if (p->key == NULL) { + zend_hash_index_del(in_hash, p->h); + } else { + if (in_hash == &EG(symbol_table)) { + zend_delete_global_variable(p->key); + } else { + zend_hash_del(in_hash, p->key); + } + } + } + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos); + + /* If there are entries to insert.. */ + if (replace) { + ZEND_HASH_FOREACH_VAL_IND(replace, entry) { + if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); + zend_hash_next_index_insert_new(&out_hash, entry); + pos++; + } ZEND_HASH_FOREACH_END(); + } + + /* Copy the remaining input hash entries to the output hash */ + for ( ; idx < in_hash->nNumUsed ; idx++) { + p = in_hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + entry = &p->val; + if (p->key == NULL) { + zend_hash_next_index_insert_new(&out_hash, entry); + } else { + zend_hash_add_new(&out_hash, p->key, entry); + } + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); + } + pos++; + } + + /* replace HashTable data */ + in_hash->u.v.nIteratorsCount = 0; + in_hash->pDestructor = NULL; + zend_hash_destroy(in_hash); + + in_hash->u.v.flags = out_hash.u.v.flags; + in_hash->nTableSize = out_hash.nTableSize; + in_hash->nTableMask = out_hash.nTableMask; + in_hash->nNumUsed = out_hash.nNumUsed; + in_hash->nNumOfElements = out_hash.nNumOfElements; + in_hash->nNextFreeElement = out_hash.nNextFreeElement; + in_hash->arData = out_hash.arData; + in_hash->pDestructor = out_hash.pDestructor; + + zend_hash_internal_pointer_reset(in_hash); } /* }}} */ @@ -1822,94 +2507,209 @@ PHPAPI void php_splice(HashTable *ht, zend_uint offset, zend_uint length, zval * Pushes elements onto the end of the array */ PHP_FUNCTION(array_push) { - zval ***args, /* Function arguments array */ + zval *args, /* Function arguments array */ *stack, /* Input array */ - *new_var; /* Variable to be pushed */ + new_var; /* Variable to be pushed */ int i, /* Loop counter */ argc; /* Number of function arguments */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) { return; } /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */ for (i = 0; i < argc; i++) { - new_var = *args[i]; - Z_ADDREF_P(new_var); + ZVAL_COPY(&new_var, &args[i]); - if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var, sizeof(zval *), NULL) == FAILURE) { - Z_DELREF_P(new_var); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to the array as the next element is already occupied"); - efree(args); + if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) { + if (Z_REFCOUNTED(new_var)) Z_DELREF(new_var); + php_error_docref(NULL, E_WARNING, "Cannot add element to the array as the next element is already occupied"); RETURN_FALSE; } } /* Clean up and return the number of values in the stack */ - efree(args); RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); } /* }}} */ -/* {{{ void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) */ -static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end) +/* {{{ proto mixed array_pop(array stack) + Pops an element off the end of the array */ +PHP_FUNCTION(array_pop) { zval *stack, /* Input stack */ - **val; /* Value to be popped */ - char *key = NULL; - uint key_len = 0; - ulong index; + *val; /* Value to be popped */ + uint32_t idx; + Bucket *p; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &stack) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &stack) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_EX(stack, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) { return; } - /* Get the first or last value and copy it into the return value */ - if (off_the_end) { - zend_hash_internal_pointer_end(Z_ARRVAL_P(stack)); - } else { - zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); + /* Get the last value and copy it into the return value */ + idx = Z_ARRVAL_P(stack)->nNumUsed; + while (1) { + if (idx == 0) { + return; + } + idx--; + p = Z_ARRVAL_P(stack)->arData + idx; + val = &p->val; + if (Z_TYPE_P(val) == IS_INDIRECT) { + val = Z_INDIRECT_P(val); + } + if (Z_TYPE_P(val) != IS_UNDEF) { + break; + } } - zend_hash_get_current_data(Z_ARRVAL_P(stack), (void **)&val); - RETVAL_ZVAL_FAST(*val); + ZVAL_DEREF(val); + ZVAL_COPY(return_value, val); - /* Delete the first or last value */ - zend_hash_get_current_key_ex(Z_ARRVAL_P(stack), &key, &key_len, &index, 0, NULL); - if (key && Z_ARRVAL_P(stack) == &EG(symbol_table)) { - zend_delete_global_variable(key, key_len - 1 TSRMLS_CC); - } else { - zend_hash_del_key_or_index(Z_ARRVAL_P(stack), key, key_len, index, (key) ? HASH_DEL_KEY : HASH_DEL_INDEX); + if (!p->key && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && p->h >= (zend_ulong)(Z_ARRVAL_P(stack)->nNextFreeElement - 1)) { + Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1; } - /* If we did a shift... re-index like it did before */ - if (!off_the_end) { - 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; + /* Delete the last value */ + if (p->key) { + if (Z_ARRVAL_P(stack) == &EG(symbol_table)) { + zend_delete_global_variable(p->key); + } else { + zend_hash_del(Z_ARRVAL_P(stack), p->key); + } + } else { + zend_hash_index_del(Z_ARRVAL_P(stack), p->h); } zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); } /* }}} */ -/* {{{ proto mixed array_pop(array stack) - Pops an element off the end of the array */ -PHP_FUNCTION(array_pop) -{ - _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - /* {{{ proto mixed array_shift(array stack) Pops an element off the beginning of the array */ PHP_FUNCTION(array_shift) { - _phpi_pop(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + zval *stack, /* Input stack */ + *val; /* Value to be popped */ + uint32_t idx; + Bucket *p; + +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/", &stack) == FAILURE) { + return; + } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_EX(stack, 0, 1) + ZEND_PARSE_PARAMETERS_END(); +#endif + + if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) { + return; + } + + /* Get the first value and copy it into the return value */ + idx = 0; + while (1) { + if (idx == Z_ARRVAL_P(stack)->nNumUsed) { + return; + } + p = Z_ARRVAL_P(stack)->arData + idx; + val = &p->val; + if (Z_TYPE_P(val) == IS_INDIRECT) { + val = Z_INDIRECT_P(val); + } + if (Z_TYPE_P(val) != IS_UNDEF) { + break; + } + idx++; + } + ZVAL_DEREF(val); + ZVAL_COPY(return_value, val); + + /* Delete the first value */ + if (p->key) { + if (Z_ARRVAL_P(stack) == &EG(symbol_table)) { + zend_delete_global_variable(p->key); + } else { + zend_hash_del(Z_ARRVAL_P(stack), p->key); + } + } else { + zend_hash_index_del(Z_ARRVAL_P(stack), p->h); + } + + /* re-index like it did before */ + if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) { + uint32_t k = 0; + + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + } + k++; + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0); + + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + if (idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } + } + k++; + } + } + Z_ARRVAL_P(stack)->nNumUsed = k; + Z_ARRVAL_P(stack)->nNextFreeElement = k; + } else { + uint32_t k = 0; + int should_rehash = 0; + + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (p->key == NULL) { + if (p->h != k) { + p->h = k++; + should_rehash = 1; + } else { + k++; + } + } + } + Z_ARRVAL_P(stack)->nNextFreeElement = k; + if (should_rehash) { + zend_hash_rehash(Z_ARRVAL_P(stack)); + } + } + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); } /* }}} */ @@ -1917,19 +2717,70 @@ PHP_FUNCTION(array_shift) Pushes elements onto the beginning of the array */ PHP_FUNCTION(array_unshift) { - zval ***args, /* Function arguments array */ + zval *args, /* Function arguments array */ *stack; /* Input stack */ + HashTable new_hash; /* New hashtable for the stack */ int argc; /* Number of function arguments */ - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) { + int i; + zend_string *key; + zval *value; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) { return; } - /* Use splice to insert the elements at the beginning. */ - php_splice(Z_ARRVAL_P(stack), 0, 0, args, argc, NULL TSRMLS_CC); + zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0); + for (i = 0; i < argc; i++) { + if (Z_REFCOUNTED(args[i])) { + Z_ADDREF(args[i]); + } + zend_hash_next_index_insert_new(&new_hash, &args[i]); + } + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { + if (key) { + zend_hash_add_new(&new_hash, key, value); + } else { + zend_hash_next_index_insert_new(&new_hash, value); + } + } ZEND_HASH_FOREACH_END(); + } else { + uint32_t old_idx; + uint32_t new_idx = i; + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0); + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { + if (key) { + zend_hash_add_new(&new_hash, key, value); + } else { + zend_hash_next_index_insert_new(&new_hash, value); + } + old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData; + if (old_idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } + new_idx++; + } ZEND_HASH_FOREACH_END(); + } + + /* replace HashTable data */ + Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0; + Z_ARRVAL_P(stack)->pDestructor = NULL; + zend_hash_destroy(Z_ARRVAL_P(stack)); + + Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags; + Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize; + Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask; + Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed; + Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements; + Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement; + Z_ARRVAL_P(stack)->arData = new_hash.arData; + Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor; + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); /* Clean up and return the number of elements in the stack */ - efree(args); RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); } /* }}} */ @@ -1939,17 +2790,13 @@ PHP_FUNCTION(array_unshift) PHP_FUNCTION(array_splice) { zval *array, /* Input array */ - **repl_array = NULL, /* Replacement array */ - ***repl = NULL; /* Replacement elements */ - HashTable *rem_hash = NULL; /* Removed elements' hash */ - Bucket *p; /* Bucket used for traversing hash */ - long i, - offset, - length = 0, - repl_num = 0; /* Number of replacement elements */ + *repl_array = NULL; /* Replacement array */ + HashTable *rem_hash = NULL; + zend_long offset, + length = 0; 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(), "a/l|lz/", &array, &offset, &length, &repl_array) == FAILURE) { return; } @@ -1959,46 +2806,37 @@ PHP_FUNCTION(array_splice) length = num_in; } - if (repl_array) { + if (ZEND_NUM_ARGS() == 4) { /* Make sure the last argument, if passed, is an array */ convert_to_array_ex(repl_array); - - /* Create the array of replacement elements */ - repl_num = zend_hash_num_elements(Z_ARRVAL_PP(repl_array)); - repl = (zval ***)safe_emalloc(repl_num, sizeof(zval **), 0); - 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) { - array_init_size(return_value, length); + if (USED_RET()) { + zend_long 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 (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) { + size = num_in - offset; + } + + /* Initialize return value */ + array_init_size(return_value, size > 0 ? (uint32_t)size : 0); rem_hash = Z_ARRVAL_P(return_value); } /* Perform splice */ - php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash TSRMLS_CC); - - /* Clean up */ - if (repl) { - efree(repl); - } + php_splice(Z_ARRVAL_P(array), (int)offset, (int)length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash); } /* }}} */ @@ -2007,31 +2845,38 @@ PHP_FUNCTION(array_splice) PHP_FUNCTION(array_slice) { zval *input, /* Input array */ - **z_length = NULL, /* How many elements to get */ - **entry; /* An array entry */ - long offset, /* Offset to get elements from */ - length = 0; + *z_length = NULL, /* How many elements to get */ + *entry; /* An array entry */ + zend_long offset, /* Offset to get elements from */ + length = 0; zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array or not */ int num_in, /* Number of elements in the input array */ pos; /* Current position in the array */ - char *string_key; - uint string_key_len; - ulong num_key; - HashPosition hpos; + zend_string *string_key; + zend_ulong num_key; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|Zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "al|zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_ARRAY(input) + Z_PARAM_LONG(offset) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(z_length) + Z_PARAM_BOOL(preserve_keys) + ZEND_PARSE_PARAMETERS_END(); +#endif /* Get number of entries in the input hash */ num_in = zend_hash_num_elements(Z_ARRVAL_P(input)); /* We want all entries from offset to the end if length is not passed or is null */ - if (ZEND_NUM_ARGS() < 3 || Z_TYPE_PP(z_length) == IS_NULL) { + if (ZEND_NUM_ARGS() < 3 || Z_TYPE_P(z_length) == IS_NULL) { length = num_in; } else { - convert_to_long_ex(z_length); - length = Z_LVAL_PP(z_length); + length = zval_get_long(z_length); } /* Clamp the offset.. */ @@ -2045,199 +2890,264 @@ PHP_FUNCTION(array_slice) /* ..and the length */ if (length < 0) { length = num_in - offset + length; - } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) { + } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) { length = num_in - offset; } - /* Initialize returned array */ - array_init_size(return_value, length > 0 ? length : 0); - if (length <= 0) { + array_init(return_value); return; } + /* Initialize returned array */ + array_init_size(return_value, (uint32_t)length); + /* Start at the beginning and go until we hit offset */ pos = 0; - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &hpos); - while (pos < offset && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) { - pos++; - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos); - } - - /* Copy elements from input array to the one that's returned */ - while (pos < offset + length && zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &hpos) == SUCCESS) { - - zval_add_ref(entry); - - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &hpos)) { - case HASH_KEY_IS_STRING: - zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL); + if (!preserve_keys && (Z_ARRVAL_P(input)->u.flags & HASH_FLAG_PACKED)) { + zend_hash_real_init(Z_ARRVAL_P(return_value), 1); + ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) { + pos++; + if (pos <= offset) { + continue; + } + if (pos > offset + length) { + break; + } + ZEND_HASH_FILL_ADD(entry); + zval_add_ref(entry); + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FILL_END(); + } else { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) { + pos++; + if (pos <= offset) { + continue; + } + if (pos > offset + length) { break; + } - case HASH_KEY_IS_LONG: + if (string_key) { + entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry); + } else { if (preserve_keys) { - zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL); + entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry); } else { - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL); + entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry); } - break; - } - pos++; - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &hpos); + } + zval_add_ref(entry); + } ZEND_HASH_FOREACH_END(); } } /* }}} */ -PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS_DC) /* {{{ */ +PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */ { - zval **src_entry, **dest_entry; - char *string_key; - uint string_key_len; - ulong num_key; - HashPosition pos; - - zend_hash_internal_pointer_reset_ex(src, &pos); - while (zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS) { - switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) { - case HASH_KEY_IS_STRING: - if (recursive && zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == SUCCESS) { - HashTable *thash = Z_TYPE_PP(dest_entry) == IS_ARRAY ? Z_ARRVAL_PP(dest_entry) : NULL; - zval *src_zval; - zval *tmp = NULL; - - if ((thash && thash->nApplyCount > 1) || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); - return 0; - } - SEPARATE_ZVAL(dest_entry); + zval *src_entry, *dest_entry; + zend_string *string_key; + + ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { + if (string_key) { + if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) { + zval *src_zval = src_entry; + zval *dest_zval = dest_entry; + HashTable *thash; + zval tmp; + int ret; + + ZVAL_DEREF(src_zval); + ZVAL_DEREF(dest_zval); + thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL; + if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { + php_error_docref(NULL, E_WARNING, "recursion detected"); + return 0; + } - if (Z_TYPE_PP(dest_entry) == IS_NULL) { - convert_to_array_ex(dest_entry); - add_next_index_null(*dest_entry); + if (Z_ISREF_P(dest_entry)) { + if (Z_REFCOUNT_P(dest_entry) == 1) { + ZVAL_UNREF(dest_entry); } else { - convert_to_array_ex(dest_entry); + Z_DELREF_P(dest_entry); + ZVAL_DUP(dest_entry, dest_zval); } - if (Z_TYPE_PP(src_entry) == IS_OBJECT) { - ALLOC_ZVAL(src_zval); - INIT_PZVAL_COPY(src_zval, *src_entry); - zval_copy_ctor(src_zval); - convert_to_array(src_zval); - tmp = src_zval; - } else { - src_zval = *src_entry; + dest_zval = dest_entry; + } else { + SEPARATE_ZVAL(dest_zval); + } + if (Z_TYPE_P(dest_zval) == IS_NULL) { + convert_to_array_ex(dest_zval); + add_next_index_null(dest_zval); + } else if (Z_TYPE_P(dest_zval) == IS_ARRAY) { + if (UNEXPECTED(Z_ARRVAL_P(dest_zval)->nNextFreeElement > Z_ARRVAL_P(dest_zval)->nNumUsed)) { + Z_ARRVAL_P(dest_zval)->nNextFreeElement = Z_ARRVAL_P(dest_zval)->nNumUsed; } - if (Z_TYPE_P(src_zval) == IS_ARRAY) { - if (thash) { - thash->nApplyCount++; - } - if (!php_array_merge(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_P(src_zval), recursive TSRMLS_CC)) { - if (thash) { - thash->nApplyCount--; - } - return 0; - } - if (thash) { - thash->nApplyCount--; - } - } else { - Z_ADDREF_PP(src_entry); - zend_hash_next_index_insert(Z_ARRVAL_PP(dest_entry), &src_zval, sizeof(zval *), NULL); + } else { + convert_to_array_ex(dest_zval); + } + ZVAL_UNDEF(&tmp); + if (Z_TYPE_P(src_zval) == IS_OBJECT) { + ZVAL_COPY(&tmp, src_zval); + convert_to_array(&tmp); + src_zval = &tmp; + } + if (Z_TYPE_P(src_zval) == IS_ARRAY) { + if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { + thash->u.v.nApplyCount++; } - if (tmp) { - zval_ptr_dtor(&tmp); + ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval)); + if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { + thash->u.v.nApplyCount--; + } + if (!ret) { + return 0; } } else { - Z_ADDREF_PP(src_entry); - zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL); + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval); } - break; - - case HASH_KEY_IS_LONG: - Z_ADDREF_PP(src_entry); - zend_hash_next_index_insert(dest, src_entry, sizeof(zval *), NULL); - break; + zval_ptr_dtor(&tmp); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_add_new(dest, string_key, src_entry); + } + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_next_index_insert_new(dest, src_entry); } - zend_hash_move_forward_ex(src, &pos); - } + } ZEND_HASH_FOREACH_END(); return 1; } /* }}} */ -PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC) /* {{{ */ +PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */ { - zval **src_entry, **dest_entry; - char *string_key; - uint string_key_len; - ulong num_key; - HashPosition pos; + zval *src_entry; + zend_string *string_key; - for (zend_hash_internal_pointer_reset_ex(src, &pos); - zend_hash_get_current_data_ex(src, (void **)&src_entry, &pos) == SUCCESS; - zend_hash_move_forward_ex(src, &pos)) { - switch (zend_hash_get_current_key_ex(src, &string_key, &string_key_len, &num_key, 0, &pos)) { - case HASH_KEY_IS_STRING: - if (Z_TYPE_PP(src_entry) != IS_ARRAY || - zend_hash_find(dest, string_key, string_key_len, (void **)&dest_entry) == FAILURE || - Z_TYPE_PP(dest_entry) != IS_ARRAY) { - - Z_ADDREF_PP(src_entry); - zend_hash_update(dest, string_key, string_key_len, src_entry, sizeof(zval *), NULL); + ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { + if (string_key) { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_update(dest, string_key, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_next_index_insert_new(dest, src_entry); + } + } ZEND_HASH_FOREACH_END(); + return 1; +} +/* }}} */ - continue; +PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */ +{ + zval *src_entry, *dest_entry, *src_zval, *dest_zval; + zend_string *string_key; + zend_ulong num_key; + int ret; + + ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) { + src_zval = src_entry; + ZVAL_DEREF(src_zval); + if (string_key) { + if (Z_TYPE_P(src_zval) != IS_ARRAY || + (dest_entry = zend_hash_find(dest, string_key)) == NULL || + (Z_TYPE_P(dest_entry) != IS_ARRAY && + (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) { + + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); } - break; + zend_hash_update(dest, string_key, src_entry); - case HASH_KEY_IS_LONG: - if (Z_TYPE_PP(src_entry) != IS_ARRAY || - zend_hash_index_find(dest, num_key, (void **)&dest_entry) == FAILURE || - Z_TYPE_PP(dest_entry) != IS_ARRAY) { - - Z_ADDREF_PP(src_entry); - zend_hash_index_update(dest, num_key, src_entry, sizeof(zval *), NULL); + continue; + } + } else { + if (Z_TYPE_P(src_zval) != IS_ARRAY || + (dest_entry = zend_hash_index_find(dest, num_key)) == NULL || + (Z_TYPE_P(dest_entry) != IS_ARRAY && + (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) { - continue; + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); } - break; + zend_hash_index_update(dest, num_key, src_entry); + + continue; + } } - if (Z_ARRVAL_PP(dest_entry)->nApplyCount > 1 || Z_ARRVAL_PP(src_entry)->nApplyCount > 1 || (*src_entry == *dest_entry && Z_ISREF_PP(dest_entry) && (Z_REFCOUNT_PP(dest_entry) % 2))) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); + dest_zval = dest_entry; + ZVAL_DEREF(dest_zval); + if (Z_ARRVAL_P(dest_zval)->u.v.nApplyCount > 1 || + Z_ARRVAL_P(src_zval)->u.v.nApplyCount > 1 || + (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { + php_error_docref(NULL, E_WARNING, "recursion detected"); return 0; } - SEPARATE_ZVAL(dest_entry); - Z_ARRVAL_PP(dest_entry)->nApplyCount++; - Z_ARRVAL_PP(src_entry)->nApplyCount++; - + SEPARATE_ZVAL(dest_zval); - if (!php_array_replace_recursive(Z_ARRVAL_PP(dest_entry), Z_ARRVAL_PP(src_entry) TSRMLS_CC)) { - Z_ARRVAL_PP(dest_entry)->nApplyCount--; - Z_ARRVAL_PP(src_entry)->nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) { + Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++; + } + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) { + Z_ARRVAL_P(src_zval)->u.v.nApplyCount++; + } + + ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval)); + + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) { + Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--; + } + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) { + Z_ARRVAL_P(src_zval)->u.v.nApplyCount--; + } + + if (!ret) { return 0; } - Z_ARRVAL_PP(dest_entry)->nApplyCount--; - Z_ARRVAL_PP(src_entry)->nApplyCount--; - } + } ZEND_HASH_FOREACH_END(); return 1; } /* }}} */ -static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */ +static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive, int replace) /* {{{ */ { - zval ***args = NULL; + zval *args = NULL; + zval *arg; int argc, i, init_size = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('+', args, argc) + ZEND_PARSE_PARAMETERS_END(); +#endif for (i = 0; i < argc; i++) { - if (Z_TYPE_PP(args[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1); - efree(args); + zval *arg = args + i; + + ZVAL_DEREF(arg); + if (Z_TYPE_P(arg) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1); RETURN_NULL(); } else { - int num = zend_hash_num_elements(Z_ARRVAL_PP(args[i])); + int num = zend_hash_num_elements(Z_ARRVAL_P(arg)); if (num > init_size) { init_size = num; @@ -2247,17 +3157,82 @@ static void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int array_init_size(return_value, init_size); - for (i = 0; i < argc; i++) { - if (!replace) { - php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), recursive TSRMLS_CC); - } else if (recursive && i > 0) { /* First array will be copied directly instead */ - php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]) TSRMLS_CC); + if (replace) { + zend_string *string_key; + zval *src_entry; + zend_ulong idx; + HashTable *src, *dest; + + /* copy first array */ + arg = args; + ZVAL_DEREF(arg); + src = Z_ARRVAL_P(arg); + dest = Z_ARRVAL_P(return_value); + ZEND_HASH_FOREACH_KEY_VAL(src, idx, string_key, src_entry) { + if (string_key) { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_add_new(dest, string_key, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_index_add_new(dest, idx, src_entry); + } + } ZEND_HASH_FOREACH_END(); + + if (recursive) { + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + php_array_replace_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg)); + } } else { - zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(args[i]), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *), 1); + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + zend_hash_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg), zval_add_ref, 1); + } } - } + } else { + zend_string *string_key; + zval *src_entry; + HashTable *src, *dest; + + /* copy first array */ + arg = args; + ZVAL_DEREF(arg); + src = Z_ARRVAL_P(arg); + dest = Z_ARRVAL_P(return_value); + ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) { + if (string_key) { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_add_new(dest, string_key, src_entry); + } else { + if (Z_REFCOUNTED_P(src_entry)) { + Z_ADDREF_P(src_entry); + } + zend_hash_next_index_insert_new(dest, src_entry); + } + } ZEND_HASH_FOREACH_END(); - efree(args); + if (recursive) { + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + php_array_merge_recursive(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg)); + } + } else { + for (i = 1; i < argc; i++) { + arg = args + i; + ZVAL_DEREF(arg); + php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(arg)); + } + } + } } /* }}} */ @@ -2299,45 +3274,70 @@ PHP_FUNCTION(array_keys) { zval *input, /* Input array */ *search_value = NULL, /* Value to search for */ - **entry, /* An entry in the input array */ - res, /* Result of comparison */ - *new_val; /* New value */ - int add_key; /* Flag to indicate whether a key should be added */ + *entry, /* An entry in the input array */ + new_val; /* New value */ zend_bool strict = 0; /* do strict comparison */ - HashPosition pos; - int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function; + zend_ulong num_idx; + zend_string *str_idx; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zb", &input, &search_value, &strict) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|zb", &input, &search_value, &strict) == FAILURE) { return; } - - if (strict) { - is_equal_func = is_identical_function; - } +#else + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ARRAY(input) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(search_value) + Z_PARAM_BOOL(strict) + ZEND_PARSE_PARAMETERS_END(); +#endif /* Initialize return array */ if (search_value != NULL) { array_init(return_value); - } else { - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); - } - add_key = 1; - /* Go through input array and add keys to the return array */ - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) { - if (search_value != NULL) { - is_equal_func(&res, search_value, *entry TSRMLS_CC); - add_key = zval_is_true(&res); + if (strict) { + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { + ZVAL_DEREF(entry); + if (fast_is_identical_function(search_value, entry)) { + if (str_idx) { + ZVAL_STR_COPY(&new_val, str_idx); + } else { + ZVAL_LONG(&new_val, num_idx); + } + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val); + } + } ZEND_HASH_FOREACH_END(); + } else { + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { + if (fast_equal_check_function(search_value, entry)) { + if (str_idx) { + ZVAL_STR_COPY(&new_val, str_idx); + } else { + ZVAL_LONG(&new_val, num_idx); + } + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val); + } + } ZEND_HASH_FOREACH_END(); } - - if (add_key) { - MAKE_STD_ZVAL(new_val); - zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(input), new_val, &pos); - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL); + } else { + array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); + if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { + return; } - - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); + 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); + } + ZEND_HASH_FILL_ADD(&new_val); + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FILL_END(); } } /* }}} */ @@ -2347,23 +3347,37 @@ PHP_FUNCTION(array_keys) PHP_FUNCTION(array_values) { zval *input, /* Input array */ - **entry; /* An entry in the input array */ - HashPosition pos; + *entry; /* An entry in the input array */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY(input) + ZEND_PARSE_PARAMETERS_END(); +#endif /* Initialize return array */ array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); - /* Go through input array and add values to the return array */ - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) { - zval_add_ref(entry); - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL); - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); + if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { + return; } + + 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) { + if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) { + entry = Z_REFVAL_P(entry); + } + Z_TRY_ADDREF_P(entry); + ZEND_HASH_FILL_ADD(entry); + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FILL_END(); } /* }}} */ @@ -2371,13 +3385,12 @@ PHP_FUNCTION(array_values) Return the value as key and the frequency of that value in input as value */ PHP_FUNCTION(array_count_values) { - zval *input, /* Input array */ - **entry, /* An entry in the input array */ - **tmp; + zval *input, /* Input array */ + *entry, /* An entry in the input array */ + *tmp; HashTable *myht; - HashPosition pos; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) { return; } @@ -2386,32 +3399,28 @@ PHP_FUNCTION(array_count_values) /* Go through input array and add values to the return array */ myht = Z_ARRVAL_P(input); - zend_hash_internal_pointer_reset_ex(myht, &pos); - while (zend_hash_get_current_data_ex(myht, (void **)&entry, &pos) == SUCCESS) { - if (Z_TYPE_PP(entry) == IS_LONG) { - if (zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), (void **)&tmp) == FAILURE) { - zval *data; - MAKE_STD_ZVAL(data); - ZVAL_LONG(data, 1); - zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL); + ZEND_HASH_FOREACH_VAL(myht, entry) { + ZVAL_DEREF(entry); + if (Z_TYPE_P(entry) == IS_LONG) { + if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) { + zval data; + ZVAL_LONG(&data, 1); + zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data); } else { - Z_LVAL_PP(tmp)++; + Z_LVAL_P(tmp)++; } - } else if (Z_TYPE_PP(entry) == IS_STRING) { - if (zend_symtable_find(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, (void**)&tmp) == FAILURE) { - zval *data; - MAKE_STD_ZVAL(data); - ZVAL_LONG(data, 1); - zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL); + } else if (Z_TYPE_P(entry) == IS_STRING) { + if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) { + zval data; + ZVAL_LONG(&data, 1); + zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data); } else { - Z_LVAL_PP(tmp)++; + Z_LVAL_P(tmp)++; } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only count STRING and INTEGER values!"); + php_error_docref(NULL, E_WARNING, "Can only count STRING and INTEGER values!"); } - - zend_hash_move_forward_ex(myht, &pos); - } + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -2419,9 +3428,9 @@ PHP_FUNCTION(array_count_values) * Specialized conversion rules for array_column() function */ static inline -zend_bool array_column_param_helper(zval **param, - const char *name TSRMLS_DC) { - switch (Z_TYPE_PP(param)) { +zend_bool array_column_param_helper(zval *param, + const char *name) { + switch (Z_TYPE_P(param)) { case IS_DOUBLE: convert_to_long_ex(param); /* fallthrough */ @@ -2435,77 +3444,88 @@ zend_bool array_column_param_helper(zval **param, return 1; default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "The %s key should be either a string or an integer", name); + php_error_docref(NULL, E_WARNING, "The %s key should be either a string or an integer", name); return 0; } } +/* }}} */ + +static inline zval *array_column_fetch_prop(zval *data, zval *name, zval *rv) +{ + zval *prop = NULL; + + if (Z_TYPE_P(data) == IS_OBJECT) { + zend_string *key = zval_get_string(name); + + if (!Z_OBJ_HANDLER_P(data, has_property) || Z_OBJ_HANDLER_P(data, has_property)(data, name, 1, NULL)) { + prop = zend_read_property(Z_OBJCE_P(data), data, ZSTR_VAL(key), ZSTR_LEN(key), 1, rv); + } + zend_string_release(key); + } else if (Z_TYPE_P(data) == IS_ARRAY) { + if (Z_TYPE_P(name) == IS_STRING) { + prop = zend_hash_find(Z_ARRVAL_P(data), Z_STR_P(name)); + } else if (Z_TYPE_P(name) == IS_LONG) { + prop = zend_hash_index_find(Z_ARRVAL_P(data), Z_LVAL_P(name)); + } + } + + return prop; +} /* {{{ proto array array_column(array input, mixed column_key[, mixed index_key]) Return the values from a single column in the input array, identified by the value_key and optionally indexed by the index_key */ PHP_FUNCTION(array_column) { - zval **zcolumn = NULL, **zkey = NULL, **data; + zval *zcolumn = NULL, *zkey = NULL, *data; HashTable *arr_hash; - HashPosition pointer; + zval *zcolval = NULL, *zkeyval = NULL, rvc, rvk; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "hZ!|Z!", &arr_hash, &zcolumn, &zkey) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "hz!|z!", &arr_hash, &zcolumn, &zkey) == FAILURE) { return; } - if ((zcolumn && !array_column_param_helper(zcolumn, "column" TSRMLS_CC)) || - (zkey && !array_column_param_helper(zkey, "index" TSRMLS_CC))) { + if ((zcolumn && !array_column_param_helper(zcolumn, "column")) || + (zkey && !array_column_param_helper(zkey, "index"))) { RETURN_FALSE; } array_init(return_value); - for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); - zend_hash_get_current_data_ex(arr_hash, (void**)&data, &pointer) == SUCCESS; - zend_hash_move_forward_ex(arr_hash, &pointer)) { - zval **zcolval, **zkeyval = NULL; - HashTable *ht; - - if (Z_TYPE_PP(data) != IS_ARRAY) { - /* Skip elemens which are not sub-arrays */ - continue; - } - ht = Z_ARRVAL_PP(data); + ZEND_HASH_FOREACH_VAL(arr_hash, data) { + ZVAL_DEREF(data); if (!zcolumn) { - /* NULL column ID means use entire subarray as data */ zcolval = data; - - /* Otherwise, skip if the value doesn't exist in our subarray */ - } else if ((Z_TYPE_PP(zcolumn) == IS_STRING) && - (zend_hash_find(ht, Z_STRVAL_PP(zcolumn), Z_STRLEN_PP(zcolumn) + 1, (void**)&zcolval) == FAILURE)) { - continue; - } else if ((Z_TYPE_PP(zcolumn) == IS_LONG) && - (zend_hash_index_find(ht, Z_LVAL_PP(zcolumn), (void**)&zcolval) == FAILURE)) { + } else if ((zcolval = array_column_fetch_prop(data, zcolumn, &rvc)) == NULL) { continue; } /* Failure will leave zkeyval alone which will land us on the final else block below * which is to append the value as next_index */ - if (zkey && (Z_TYPE_PP(zkey) == IS_STRING)) { - zend_hash_find(ht, Z_STRVAL_PP(zkey), Z_STRLEN_PP(zkey) + 1, (void**)&zkeyval); - } else if (zkey && (Z_TYPE_PP(zkey) == IS_LONG)) { - zend_hash_index_find(ht, Z_LVAL_PP(zkey), (void**)&zkeyval); - } - - Z_ADDREF_PP(zcolval); - if (zkeyval && Z_TYPE_PP(zkeyval) == IS_STRING) { - add_assoc_zval(return_value, Z_STRVAL_PP(zkeyval), *zcolval); - } else if (zkeyval && Z_TYPE_PP(zkeyval) == IS_LONG) { - add_index_zval(return_value, Z_LVAL_PP(zkeyval), *zcolval); - } else if (zkeyval && Z_TYPE_PP(zkeyval) == IS_OBJECT) { - SEPARATE_ZVAL(zkeyval); - convert_to_string(*zkeyval); - add_assoc_zval(return_value, Z_STRVAL_PP(zkeyval), *zcolval); + if (zkey) { + zkeyval = array_column_fetch_prop(data, zkey, &rvk); + } + + Z_TRY_ADDREF_P(zcolval); + if (zkeyval && Z_TYPE_P(zkeyval) == IS_STRING) { + zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(zkeyval), zcolval); + } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_LONG) { + add_index_zval(return_value, Z_LVAL_P(zkeyval), zcolval); + } else if (zkeyval && Z_TYPE_P(zkeyval) == IS_OBJECT) { + zend_string *key = zval_get_string(zkeyval); + zend_symtable_update(Z_ARRVAL_P(return_value), key, zcolval); + zend_string_release(key); } else { - add_next_index_zval(return_value, *zcolval); + add_next_index_zval(return_value, zcolval); } - } + if (zcolval == &rvc) { + zval_ptr_dtor(&rvc); + } + if (zkeyval == &rvk) { + zval_ptr_dtor(&rvk); + } + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -2514,40 +3534,31 @@ PHP_FUNCTION(array_column) PHP_FUNCTION(array_reverse) { zval *input, /* Input array */ - **entry; /* An entry in the input array */ - char *string_key; - uint string_key_len; - ulong num_key; + *entry; /* An entry in the input array */ + zend_string *string_key; + zend_ulong num_key; zend_bool preserve_keys = 0; /* whether to preserve keys */ - HashPosition pos; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|b", &input, &preserve_keys) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &input, &preserve_keys) == FAILURE) { return; } /* Initialize return array */ array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); - zend_hash_internal_pointer_end_ex(Z_ARRVAL_P(input), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS) { - zval_add_ref(entry); - - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) { - case HASH_KEY_IS_STRING: - zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, entry, sizeof(zval *), NULL); - break; - - case HASH_KEY_IS_LONG: - if (preserve_keys) { - zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(zval *), NULL); - } else { - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), entry, sizeof(zval *), NULL); - } - break; + ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) { + if (string_key) { + entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry); + } else { + if (preserve_keys) { + entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry); + } else { + entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry); + } } - zend_hash_move_backwards_ex(Z_ARRVAL_P(input), &pos); - } + zval_add_ref(entry); + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -2557,57 +3568,60 @@ PHP_FUNCTION(array_pad) { zval *input; /* Input array */ zval *pad_value; /* Padding value obviously */ - zval ***pads; /* Array to pass to splice */ - long pad_size; /* Size to pad to */ - long pad_size_abs; /* Absolute value of pad_size */ - int input_size; /* Size of the input array */ - int num_pads; /* How many pads do we need */ - int do_pad; /* Whether we should do padding at all */ - int i; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "alz", &input, &pad_size, &pad_value) == FAILURE) { + zend_long pad_size; /* Size to pad to */ + zend_long pad_size_abs; /* Absolute value of pad_size */ + zend_long input_size; /* Size of the input array */ + zend_long num_pads; /* How many pads do we need */ + zend_long i; + zend_string *key; + zval *value; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "alz", &input, &pad_size, &pad_value) == FAILURE) { return; } /* Do some initial calculations */ input_size = zend_hash_num_elements(Z_ARRVAL_P(input)); - pad_size_abs = abs(pad_size); - if (pad_size_abs < 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time"); - zval_dtor(return_value); + pad_size_abs = ZEND_ABS(pad_size); + if (pad_size_abs < 0 || pad_size_abs - input_size > Z_L(1048576)) { + php_error_docref(NULL, E_WARNING, "You may only pad up to 1048576 elements at a time"); RETURN_FALSE; } - do_pad = (input_size >= pad_size_abs) ? 0 : 1; - - /* Copy the original array */ - RETVAL_ZVAL(input, 1, 0); - /* If no need to pad, no need to continue */ - if (!do_pad) { + if (input_size >= pad_size_abs) { + /* Copy the original array */ + ZVAL_COPY(return_value, input); return; } - /* Populate the pads array */ num_pads = pad_size_abs - input_size; - if (num_pads > 1048576) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "You may only pad up to 1048576 elements at a time"); - zval_dtor(return_value); - RETURN_FALSE; + array_init_size(return_value, pad_size_abs); + if (Z_REFCOUNTED_P(pad_value)) { + GC_REFCOUNT(Z_COUNTED_P(pad_value)) += num_pads; } - pads = (zval ***)safe_emalloc(num_pads, sizeof(zval **), 0); - for (i = 0; i < num_pads; i++) { - pads[i] = &pad_value; + + if (pad_size < 0) { + for (i = 0; i < num_pads; i++) { + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value); + } } - /* Pad on the right or on the left */ + ZEND_HASH_FOREACH_STR_KEY_VAL_IND(Z_ARRVAL_P(input), key, value) { + if (Z_REFCOUNTED_P(value)) { + Z_ADDREF_P(value); + } + if (key) { + zend_hash_add_new(Z_ARRVAL_P(return_value), key, value); + } else { + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value); + } + } ZEND_HASH_FOREACH_END(); + if (pad_size > 0) { - php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL TSRMLS_CC); - } else { - php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL TSRMLS_CC); + for (i = 0; i < num_pads; i++) { + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value); + } } - - /* Clean up */ - efree(pads); } /* }}} */ @@ -2615,31 +3629,36 @@ PHP_FUNCTION(array_pad) Return array with key <-> value flipped */ PHP_FUNCTION(array_flip) { - zval *array, **entry, *data; - HashPosition pos; + zval *array, *entry, data; + zend_ulong num_idx; + zend_string *str_idx; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &array) == FAILURE) { return; } array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array))); - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) { - MAKE_STD_ZVAL(data); - zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), data, &pos); - - if (Z_TYPE_PP(entry) == IS_LONG) { - zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL); - } else if (Z_TYPE_PP(entry) == IS_STRING) { - zend_symtable_update(Z_ARRVAL_P(return_value), Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &data, sizeof(data), NULL); + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { + ZVAL_DEREF(entry); + if (Z_TYPE_P(entry) == IS_LONG) { + if (str_idx) { + ZVAL_STR_COPY(&data, str_idx); + } else { + ZVAL_LONG(&data, num_idx); + } + zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data); + } else if (Z_TYPE_P(entry) == IS_STRING) { + if (str_idx) { + ZVAL_STR_COPY(&data, str_idx); + } else { + ZVAL_LONG(&data, num_idx); + } + zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data); } else { - zval_ptr_dtor(&data); /* will free also zval structure */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can only flip STRING and INTEGER values!"); + php_error_docref(NULL, E_WARNING, "Can only flip STRING and INTEGER values!"); } - - zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos); - } + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -2647,42 +3666,49 @@ PHP_FUNCTION(array_flip) Retuns an array with all string keys lowercased [or uppercased] */ PHP_FUNCTION(array_change_key_case) { - zval *array, **entry; - char *string_key; - char *new_key; - uint str_key_len; - ulong num_key; - long change_to_upper=0; - HashPosition pos; + zval *array, *entry; + zend_string *string_key; + zend_string *new_key; + zend_ulong num_key; + zend_long change_to_upper=0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &change_to_upper) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &change_to_upper) == FAILURE) { return; } array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array))); - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) { + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) { + if (!string_key) { + entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry); + } else { + if (change_to_upper) { + new_key = php_string_toupper(string_key); + } else { + new_key = php_string_tolower(string_key); + } + entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry); + zend_string_release(new_key); + } + zval_add_ref(entry); + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) { - case HASH_KEY_IS_LONG: - zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry, sizeof(entry), NULL); - break; - case HASH_KEY_IS_STRING: - new_key = estrndup(string_key, str_key_len - 1); - if (change_to_upper) { - php_strtoupper(new_key, str_key_len - 1); - } else { - php_strtolower(new_key, str_key_len - 1); - } - zend_hash_update(Z_ARRVAL_P(return_value), new_key, str_key_len, entry, sizeof(entry), NULL); - efree(new_key); - break; - } +struct bucketindex { + Bucket b; + unsigned int i; +}; - zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos); - } +static void array_bucketindex_swap(void *p, void *q) /* {{{ */ +{ + struct bucketindex *f = (struct bucketindex *)p; + struct bucketindex *g = (struct bucketindex *)q; + struct bucketindex t; + t = *f; + *f = *g; + *g = t; } /* }}} */ @@ -2690,124 +3716,95 @@ PHP_FUNCTION(array_change_key_case) Removes duplicate values from array */ PHP_FUNCTION(array_unique) { - zval *array, *tmp; + zval *array; + uint idx; Bucket *p; - struct bucketindex { - Bucket *b; - unsigned int i; - }; struct bucketindex *arTmp, *cmpdata, *lastkept; unsigned int i; - long sort_type = PHP_SORT_STRING; + zend_long sort_type = PHP_SORT_STRING; + compare_func_t cmp; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &array, &sort_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &array, &sort_type) == FAILURE) { return; } - php_set_compare_func(sort_type TSRMLS_CC); + cmp = php_get_data_compare_func(sort_type, 0); - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array))); - zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_P(array), (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval*)); if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */ + ZVAL_COPY(return_value, array); return; } + RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array))); + /* create and sort array with pointers to the target_hash buckets */ - arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->persistent); + arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT); if (!arTmp) { zval_dtor(return_value); RETURN_FALSE; } - for (i = 0, p = Z_ARRVAL_P(array)->pListHead; p; i++, p = p->pListNext) { - arTmp[i].b = p; + for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) { + p = Z_ARRVAL_P(array)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (Z_TYPE(p->val) == IS_INDIRECT && Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) continue; + arTmp[i].b = *p; arTmp[i].i = i; + i++; } - arTmp[i].b = NULL; - zend_qsort((void *) arTmp, i, sizeof(struct bucketindex), php_array_data_compare TSRMLS_CC); - + ZVAL_UNDEF(&arTmp[i].b.val); + zend_sort((void *) arTmp, i, sizeof(struct bucketindex), + cmp, (swap_func_t)array_bucketindex_swap); /* go through the sorted array and delete duplicates from the copy */ lastkept = arTmp; - for (cmpdata = arTmp + 1; cmpdata->b; cmpdata++) { - if (php_array_data_compare(lastkept, cmpdata TSRMLS_CC)) { + for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) { + if (cmp(lastkept, cmpdata)) { lastkept = cmpdata; } else { if (lastkept->i > cmpdata->i) { - p = lastkept->b; + p = &lastkept->b; lastkept = cmpdata; } else { - p = cmpdata->b; + p = &cmpdata->b; } - if (p->nKeyLength == 0) { + if (p->key == NULL) { zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); } else { if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) { - zend_delete_global_variable(p->arKey, p->nKeyLength - 1 TSRMLS_CC); + zend_delete_global_variable(p->key); } else { - zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h); + zend_hash_del(Z_ARRVAL_P(return_value), p->key); } } } } - pefree(arTmp, Z_ARRVAL_P(array)->persistent); + pefree(arTmp, Z_ARRVAL_P(array)->u.flags & HASH_FLAG_PERSISTENT); } /* }}} */ -static int zval_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */ +static int zval_compare(zval *first, zval *second) /* {{{ */ { - zval result; - zval *first; - zval *second; - - first = *((zval **) a); - second = *((zval **) b); - - if (string_compare_function(&result, first, second TSRMLS_CC) == FAILURE) { - return 0; - } - - if (Z_TYPE(result) == IS_DOUBLE) { - if (Z_DVAL(result) < 0) { - return -1; - } else if (Z_DVAL(result) > 0) { - return 1; - } else { - return 0; - } - } - - convert_to_long(&result); - - if (Z_LVAL(result) < 0) { - return -1; - } else if (Z_LVAL(result) > 0) { - return 1; - } - - return 0; + return string_compare_function(first, second); } /* }}} */ -static int zval_user_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */ +static int zval_user_compare(zval *a, zval *b) /* {{{ */ { - zval **args[2]; - zval *retval_ptr = NULL; + zval args[2]; + zval retval; - args[0] = (zval **) a; - args[1] = (zval **) b; + ZVAL_COPY_VALUE(&args[0], a); + ZVAL_COPY_VALUE(&args[1], b); BG(user_compare_fci).param_count = 2; BG(user_compare_fci).params = args; - BG(user_compare_fci).retval_ptr_ptr = &retval_ptr; + BG(user_compare_fci).retval = &retval; BG(user_compare_fci).no_separation = 0; - if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) { - long retval; - - convert_to_long_ex(&retval_ptr); - retval = Z_LVAL_P(retval_ptr); - zval_ptr_dtor(&retval_ptr); - return retval < 0 ? -1 : retval > 0 ? 1 : 0;; + if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { + zend_long ret = zval_get_long(&retval); + zval_ptr_dtor(&retval); + return ret < 0 ? -1 : ret > 0 ? 1 : 0;; } else { return 0; } @@ -2816,12 +3813,13 @@ static int zval_user_compare(zval **a, zval **b TSRMLS_DC) /* {{{ */ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */ { + uint idx; Bucket *p; int argc, i; - zval ***args; - int (*intersect_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL; + zval *args; + int (*intersect_data_compare_func)(zval *, zval *) = NULL; zend_bool ok; - zval **data; + zval *val, *data; int req_args; char *param_spec; @@ -2837,76 +3835,88 @@ static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compa INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */ req_args = 2; param_spec = "+"; - + if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) { intersect_data_compare_func = zval_compare; } } - + if (argc < req_args) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, argc); + php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, argc); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { return; } for (i = 0; i < argc; i++) { - if (Z_TYPE_PP(args[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1); - RETVAL_NULL(); - goto out; + if (Z_TYPE(args[i]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1); + RETURN_NULL(); } } array_init(return_value); - for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) { - if (p->nKeyLength == 0) { + for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) { + p = Z_ARRVAL(args[0])->arData + idx; + val = &p->val; + if (Z_TYPE_P(val) == IS_UNDEF) continue; + if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) { + val = Z_INDIRECT_P(val); + if (Z_TYPE_P(val) == IS_UNDEF) continue; + } + if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) { + ZVAL_UNREF(val); + } + if (p->key == NULL) { ok = 1; for (i = 1; i < argc; i++) { - if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == FAILURE || + if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) == NULL || (intersect_data_compare_func && - intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0) + intersect_data_compare_func(val, data) != 0) ) { ok = 0; break; } } if (ok) { - Z_ADDREF_PP((zval**)p->pData); - zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL); + if (Z_REFCOUNTED_P(val)) { + Z_ADDREF_P(val); + } + zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val); } } else { ok = 1; for (i = 1; i < argc; i++) { - if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == FAILURE || + if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) == NULL || (intersect_data_compare_func && - intersect_data_compare_func((zval**)p->pData, data TSRMLS_CC) != 0) + intersect_data_compare_func(val, data) != 0) ) { ok = 0; break; } } if (ok) { - Z_ADDREF_PP((zval**)p->pData); - zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL); + if (Z_REFCOUNTED_P(val)) { + Z_ADDREF_P(val); + } + zend_hash_update(Z_ARRVAL_P(return_value), p->key, val); } } } -out: - efree(args); } /* }}} */ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */ { - zval ***args = NULL; + zval *args = NULL; HashTable *hash; int arr_argc, i, c = 0; - Bucket ***lists, **list, ***ptrs, *p; - int req_args; + uint idx; + Bucket **lists, *list, **ptrs, *p; + uint32_t req_args; char *param_spec; zend_fcall_info fci1, fci2; zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache; @@ -2914,33 +3924,33 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache; PHP_ARRAY_CMP_FUNC_VARS; - int (*intersect_key_compare_func)(const void *, const void * TSRMLS_DC); - int (*intersect_data_compare_func)(const void *, const void * TSRMLS_DC); + int (*intersect_key_compare_func)(const void *, const void *); + int (*intersect_data_compare_func)(const void *, const void *); if (behavior == INTERSECT_NORMAL) { - intersect_key_compare_func = php_array_key_compare; + intersect_key_compare_func = php_array_key_compare_string; if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) { /* array_intersect() */ req_args = 2; param_spec = "+"; - intersect_data_compare_func = php_array_data_compare; + intersect_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == INTERSECT_COMP_DATA_USER) { /* array_uintersect() */ req_args = 3; param_spec = "+f"; intersect_data_compare_func = php_array_user_compare; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type); + php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type); return; } if (ZEND_NUM_ARGS() < req_args) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); + php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) { return; } fci_data = &fci1; @@ -2949,19 +3959,19 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former * no comparison of the data is done (part of INTERSECT_ASSOC) */ - intersect_key_compare_func = php_array_key_compare; + intersect_key_compare_func = php_array_key_compare_string; if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) { /* array_intersect_assoc() or array_intersect_key() */ req_args = 2; param_spec = "+"; - intersect_key_compare_func = php_array_key_compare; - intersect_data_compare_func = php_array_data_compare; + intersect_key_compare_func = php_array_key_compare_string; + intersect_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) { /* array_uintersect_assoc() */ req_args = 3; param_spec = "+f"; - intersect_key_compare_func = php_array_key_compare; + intersect_key_compare_func = php_array_key_compare_string; intersect_data_compare_func = php_array_user_compare; fci_data = &fci1; fci_data_cache = &fci1_cache; @@ -2970,7 +3980,7 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int req_args = 3; param_spec = "+f"; intersect_key_compare_func = php_array_user_key_compare; - intersect_data_compare_func = php_array_data_compare; + intersect_data_compare_func = php_array_data_compare_string; fci_key = &fci1; fci_key_cache = &fci1_cache; } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) { @@ -2984,30 +3994,29 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int fci_key = &fci2; fci_key_cache = &fci2_cache; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type); + php_error_docref(NULL, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type); return; } if (ZEND_NUM_ARGS() < req_args) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); + php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) { return; } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior); + php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior); return; } PHP_ARRAY_CMP_FUNC_BACKUP(); /* for each argument, create and sort list with pointers to the hash buckets */ - lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0); - ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0); - php_set_compare_func(PHP_SORT_STRING TSRMLS_CC); + lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); + ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) { BG(user_compare_fci) = *fci_data; @@ -3018,66 +4027,60 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int } for (i = 0; i < arr_argc; i++) { - if (Z_TYPE_PP(args[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1); + if (Z_TYPE(args[i]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1); arr_argc = i; /* only free up to i - 1 */ goto out; } - hash = Z_ARRVAL_PP(args[i]); - list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent); + hash = Z_ARRVAL(args[i]); + list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT); if (!list) { PHP_ARRAY_CMP_FUNC_RESTORE(); efree(ptrs); efree(lists); - efree(args); RETURN_FALSE; } lists[i] = list; ptrs[i] = list; - for (p = hash->pListHead; p; p = p->pListNext) { - *list++ = p; + for (idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + *list++ = *p; } - *list = NULL; - if (behavior == INTERSECT_NORMAL) { - zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_data_compare_func TSRMLS_CC); - } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ - zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_key_compare_func TSRMLS_CC); + ZVAL_UNDEF(&list->val); + if (hash->nNumOfElements > 1) { + if (behavior == INTERSECT_NORMAL) { + zend_sort((void *) lists[i], hash->nNumOfElements, + sizeof(Bucket), intersect_data_compare_func, (swap_func_t)zend_hash_bucket_swap); + } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ + zend_sort((void *) lists[i], hash->nNumOfElements, + sizeof(Bucket), intersect_key_compare_func, (swap_func_t)zend_hash_bucket_swap); + } } } /* copy the argument array */ - RETVAL_ZVAL(*args[0], 1, 0); - if (return_value->value.ht == &EG(symbol_table)) { - HashTable *ht; - zval *tmp; - - ALLOC_HASHTABLE(ht); - zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); - return_value->value.ht = ht; - } + RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0]))); /* go through the lists and look for common values */ - while (*ptrs[0]) { + while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) { if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */ - && - key_compare_type == INTERSECT_COMP_KEY_USER) { - + && key_compare_type == INTERSECT_COMP_KEY_USER) { BG(user_compare_fci) = *fci_key; BG(user_compare_fci_cache) = *fci_key_cache; } for (i = 1; i < arr_argc; i++) { if (behavior & INTERSECT_NORMAL) { - while (*ptrs[i] && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) { + while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) { ptrs[i]++; } } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ - while (*ptrs[i] && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) { + while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) { ptrs[i]++; } - if ((!c && *ptrs[i]) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */ + if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */ /* this means that ptrs[i] is not NULL so we can compare * and "c==0" is from last operation * in this branch of code we enter only when INTERSECT_ASSOC @@ -3086,7 +4089,7 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int BG(user_compare_fci) = *fci_data; BG(user_compare_fci_cache) = *fci_data_cache; } - if (intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC) != 0) { + if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) { c = 1; if (key_compare_type == INTERSECT_COMP_KEY_USER) { BG(user_compare_fci) = *fci_key; @@ -3099,19 +4102,19 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int } } } - if (!*ptrs[i]) { + if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) { /* delete any values corresponding to remains of ptrs[0] */ /* and exit because they do not present in at least one of */ /* the other arguments */ for (;;) { - p = *ptrs[0]++; - if (!p) { + p = ptrs[0]++; + if (Z_TYPE(p->val) == IS_UNDEF) { goto out; } - if (p->nKeyLength == 0) { + if (p->key == NULL) { zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); } else { - zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h); + zend_hash_del(Z_ARRVAL_P(return_value), p->key); } } } @@ -3123,17 +4126,17 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int /* Value of ptrs[0] not in all arguments, delete all entries */ /* with value < value of ptrs[i] */ for (;;) { - p = *ptrs[0]; - if (p->nKeyLength == 0) { + p = ptrs[0]; + if (p->key == NULL) { zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); } else { - zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h); + zend_hash_del(Z_ARRVAL_P(return_value), p->key); } - if (!*++ptrs[0]) { + if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) { goto out; } if (behavior == INTERSECT_NORMAL) { - if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)) { + if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) { break; } } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ @@ -3145,11 +4148,11 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int /* ptrs[0] is present in all the arguments */ /* Skip all entries with same value as ptrs[0] */ for (;;) { - if (!*++ptrs[0]) { + if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) { goto out; } if (behavior == INTERSECT_NORMAL) { - if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) { + if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) { break; } } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */ @@ -3161,15 +4164,14 @@ static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int } out: for (i = 0; i < arr_argc; i++) { - hash = Z_ARRVAL_PP(args[i]); - pefree(lists[i], hash->persistent); + hash = Z_ARRVAL(args[i]); + pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT); } PHP_ARRAY_CMP_FUNC_RESTORE(); efree(ptrs); efree(lists); - efree(args); } /* }}} */ @@ -3198,7 +4200,7 @@ PHP_FUNCTION(array_intersect) /* }}} */ /* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func) - Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using an user-supplied callback. */ + Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using a user-supplied callback. */ PHP_FUNCTION(array_uintersect) { php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL); @@ -3214,7 +4216,7 @@ PHP_FUNCTION(array_intersect_assoc) /* }}} */ /* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U - Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using an user-supplied callback. */ + Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using a user-supplied callback. */ PHP_FUNCTION(array_intersect_uassoc) { php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER); @@ -3222,7 +4224,7 @@ PHP_FUNCTION(array_intersect_uassoc) /* }}} */ /* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U - Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using an user-supplied callback. */ + Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using a user-supplied callback. */ PHP_FUNCTION(array_uintersect_assoc) { php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER); @@ -3239,30 +4241,31 @@ PHP_FUNCTION(array_uintersect_uassoc) static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */ { + uint idx; Bucket *p; int argc, i; - zval ***args; - int (*diff_data_compare_func)(zval **, zval ** TSRMLS_DC) = NULL; + zval *args; + int (*diff_data_compare_func)(zval *, zval *) = NULL; zend_bool ok; - zval **data; + zval *val, *data; /* Get the argument count */ argc = ZEND_NUM_ARGS(); if (data_compare_type == DIFF_COMP_DATA_USER) { if (argc < 3) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS()); + php_error_docref(NULL, E_WARNING, "at least 3 parameters are required, %d given", ZEND_NUM_ARGS()); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) { return; } diff_data_compare_func = zval_user_compare; } else { if (argc < 2) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS()); + php_error_docref(NULL, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS()); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { return; } if (data_compare_type == DIFF_COMP_DATA_INTERNAL) { @@ -3271,60 +4274,72 @@ static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_ty } for (i = 0; i < argc; i++) { - if (Z_TYPE_PP(args[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1); - RETVAL_NULL(); - goto out; + if (Z_TYPE(args[i]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1); + RETURN_NULL(); } } array_init(return_value); - for (p = Z_ARRVAL_PP(args[0])->pListHead; p != NULL; p = p->pListNext) { - if (p->nKeyLength == 0) { + for (idx = 0; idx < Z_ARRVAL(args[0])->nNumUsed; idx++) { + p = Z_ARRVAL(args[0])->arData + idx; + val = &p->val; + if (Z_TYPE_P(val) == IS_UNDEF) continue; + if (UNEXPECTED(Z_TYPE_P(val) == IS_INDIRECT)) { + val = Z_INDIRECT_P(val); + if (Z_TYPE_P(val) == IS_UNDEF) continue; + } + if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) { + ZVAL_UNREF(val); + } + if (p->key == NULL) { ok = 1; for (i = 1; i < argc; i++) { - if (zend_hash_index_find(Z_ARRVAL_PP(args[i]), p->h, (void**)&data) == SUCCESS && + if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), p->h)) != NULL && (!diff_data_compare_func || - diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0) + diff_data_compare_func(val, data) == 0) ) { ok = 0; break; } } if (ok) { - Z_ADDREF_PP((zval**)p->pData); - zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, p->pData, sizeof(zval*), NULL); + if (Z_REFCOUNTED_P(val)) { + Z_ADDREF_P(val); + } + zend_hash_index_update(Z_ARRVAL_P(return_value), p->h, val); } } else { ok = 1; for (i = 1; i < argc; i++) { - if (zend_hash_quick_find(Z_ARRVAL_PP(args[i]), p->arKey, p->nKeyLength, p->h, (void**)&data) == SUCCESS && + if ((data = zend_hash_find_ind(Z_ARRVAL(args[i]), p->key)) != NULL && (!diff_data_compare_func || - diff_data_compare_func((zval**)p->pData, data TSRMLS_CC) == 0) + diff_data_compare_func(val, data) == 0) ) { ok = 0; break; } } if (ok) { - Z_ADDREF_PP((zval**)p->pData); - zend_hash_quick_update(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h, p->pData, sizeof(zval*), NULL); + if (Z_REFCOUNTED_P(val)) { + Z_ADDREF_P(val); + } + zend_hash_update(Z_ARRVAL_P(return_value), p->key, val); } } } -out: - efree(args); } /* }}} */ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */ { - zval ***args = NULL; + zval *args = NULL; HashTable *hash; int arr_argc, i, c; - Bucket ***lists, **list, ***ptrs, *p; - int req_args; + uint idx; + Bucket **lists, *list, **ptrs, *p; + uint32_t req_args; char *param_spec; zend_fcall_info fci1, fci2; zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache; @@ -3332,33 +4347,33 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache; PHP_ARRAY_CMP_FUNC_VARS; - int (*diff_key_compare_func)(const void *, const void * TSRMLS_DC); - int (*diff_data_compare_func)(const void *, const void * TSRMLS_DC); + int (*diff_key_compare_func)(const void *, const void *); + int (*diff_data_compare_func)(const void *, const void *); if (behavior == DIFF_NORMAL) { - diff_key_compare_func = php_array_key_compare; + diff_key_compare_func = php_array_key_compare_string; if (data_compare_type == DIFF_COMP_DATA_INTERNAL) { /* array_diff */ req_args = 2; param_spec = "+"; - diff_data_compare_func = php_array_data_compare; + diff_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == DIFF_COMP_DATA_USER) { /* array_udiff */ req_args = 3; param_spec = "+f"; diff_data_compare_func = php_array_user_compare; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type); + php_error_docref(NULL, E_WARNING, "data_compare_type is %d. This should never happen. Please report as a bug", data_compare_type); return; } if (ZEND_NUM_ARGS() < req_args) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); + php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) { return; } fci_data = &fci1; @@ -3372,13 +4387,13 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ /* array_diff_assoc() or array_diff_key() */ req_args = 2; param_spec = "+"; - diff_key_compare_func = php_array_key_compare; - diff_data_compare_func = php_array_data_compare; + diff_key_compare_func = php_array_key_compare_string; + diff_data_compare_func = php_array_data_compare_string; } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) { /* array_udiff_assoc() */ req_args = 3; param_spec = "+f"; - diff_key_compare_func = php_array_key_compare; + diff_key_compare_func = php_array_key_compare_string; diff_data_compare_func = php_array_user_compare; fci_data = &fci1; fci_data_cache = &fci1_cache; @@ -3387,7 +4402,7 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ req_args = 3; param_spec = "+f"; diff_key_compare_func = php_array_user_key_compare; - diff_data_compare_func = php_array_data_compare; + diff_data_compare_func = php_array_data_compare_string; fci_key = &fci1; fci_key_cache = &fci1_cache; } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) { @@ -3401,30 +4416,29 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ fci_key = &fci2; fci_key_cache = &fci2_cache; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type); + php_error_docref(NULL, E_WARNING, "data_compare_type is %d. key_compare_type is %d. This should never happen. Please report as a bug", data_compare_type, key_compare_type); return; } if (ZEND_NUM_ARGS() < req_args) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); + php_error_docref(NULL, E_WARNING, "at least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS()); return; } - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) { return; } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior); + php_error_docref(NULL, E_WARNING, "behavior is %d. This should never happen. Please report as a bug", behavior); return; } PHP_ARRAY_CMP_FUNC_BACKUP(); /* for each argument, create and sort list with pointers to the hash buckets */ - lists = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0); - ptrs = (Bucket ***)safe_emalloc(arr_argc, sizeof(Bucket **), 0); - php_set_compare_func(PHP_SORT_STRING TSRMLS_CC); + lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); + ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0); if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) { BG(user_compare_fci) = *fci_data; @@ -3435,48 +4449,44 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ } for (i = 0; i < arr_argc; i++) { - if (Z_TYPE_PP(args[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is not an array", i + 1); + if (Z_TYPE(args[i]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1); arr_argc = i; /* only free up to i - 1 */ goto out; } - hash = Z_ARRVAL_PP(args[i]); - list = (Bucket **) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket *), hash->persistent); + hash = Z_ARRVAL(args[i]); + list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), hash->u.flags & HASH_FLAG_PERSISTENT); if (!list) { PHP_ARRAY_CMP_FUNC_RESTORE(); efree(ptrs); efree(lists); - efree(args); RETURN_FALSE; } lists[i] = list; ptrs[i] = list; - for (p = hash->pListHead; p; p = p->pListNext) { - *list++ = p; + for (idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + *list++ = *p; } - *list = NULL; - if (behavior == DIFF_NORMAL) { - zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_data_compare_func TSRMLS_CC); - } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */ - zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), diff_key_compare_func TSRMLS_CC); + ZVAL_UNDEF(&list->val); + if (hash->nNumOfElements > 1) { + if (behavior == DIFF_NORMAL) { + zend_sort((void *) lists[i], hash->nNumOfElements, + sizeof(Bucket), diff_data_compare_func, (swap_func_t)zend_hash_bucket_swap); + } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */ + zend_sort((void *) lists[i], hash->nNumOfElements, + sizeof(Bucket), diff_key_compare_func, (swap_func_t)zend_hash_bucket_swap); + } } } /* copy the argument array */ - RETVAL_ZVAL(*args[0], 1, 0); - if (return_value->value.ht == &EG(symbol_table)) { - HashTable *ht; - zval *tmp; - - ALLOC_HASHTABLE(ht); - zend_hash_init(ht, zend_hash_num_elements(return_value->value.ht), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(ht, return_value->value.ht, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); - return_value->value.ht = ht; - } + RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0]))); /* go through the lists and look for values of ptr[0] that are not in the others */ - while (*ptrs[0]) { + while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) { if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */ && key_compare_type == DIFF_COMP_KEY_USER @@ -3486,31 +4496,31 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ } c = 1; for (i = 1; i < arr_argc; i++) { - Bucket **ptr = ptrs[i]; + Bucket *ptr = ptrs[i]; if (behavior == DIFF_NORMAL) { - while (*ptrs[i] && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i] TSRMLS_CC)))) { + while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) { ptrs[i]++; } } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */ - while (*ptr && (0 != (c = diff_key_compare_func(ptrs[0], ptr TSRMLS_CC)))) { + while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) { ptr++; } } if (!c) { if (behavior == DIFF_NORMAL) { - if (*ptrs[i]) { + if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) { ptrs[i]++; } break; } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */ /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY * data comparison is not needed - skipped. */ - if (*ptr) { + if (Z_TYPE(ptr->val) != IS_UNDEF) { if (data_compare_type == DIFF_COMP_DATA_USER) { BG(user_compare_fci) = *fci_data; BG(user_compare_fci_cache) = *fci_data_cache; } - if (diff_data_compare_func(ptrs[0], ptr TSRMLS_CC) != 0) { + if (diff_data_compare_func(ptrs[0], ptr) != 0) { /* the data is not the same */ c = -1; if (key_compare_type == DIFF_COMP_KEY_USER) { @@ -3536,17 +4546,17 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ /* ptrs[0] in one of the other arguments */ /* delete all entries with value as ptrs[0] */ for (;;) { - p = *ptrs[0]; - if (p->nKeyLength == 0) { + p = ptrs[0]; + if (p->key == NULL) { zend_hash_index_del(Z_ARRVAL_P(return_value), p->h); } else { - zend_hash_quick_del(Z_ARRVAL_P(return_value), p->arKey, p->nKeyLength, p->h); + zend_hash_del(Z_ARRVAL_P(return_value), p->key); } - if (!*++ptrs[0]) { + if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) { goto out; } if (behavior == DIFF_NORMAL) { - if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) { + if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) { break; } } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */ @@ -3558,11 +4568,11 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ /* ptrs[0] in none of the other arguments */ /* skip all entries with value as ptrs[0] */ for (;;) { - if (!*++ptrs[0]) { + if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) { goto out; } if (behavior == DIFF_NORMAL) { - if (diff_data_compare_func(ptrs[0] - 1, ptrs[0] TSRMLS_CC)) { + if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) { break; } } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */ @@ -3574,15 +4584,14 @@ static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_ } out: for (i = 0; i < arr_argc; i++) { - hash = Z_ARRVAL_PP(args[i]); - pefree(lists[i], hash->persistent); + hash = Z_ARRVAL(args[i]); + pefree(lists[i], hash->u.flags & HASH_FLAG_PERSISTENT); } PHP_ARRAY_CMP_FUNC_RESTORE(); efree(ptrs); efree(lists); - efree(args); } /* }}} */ @@ -3606,7 +4615,71 @@ PHP_FUNCTION(array_diff_ukey) Returns the entries of arr1 that have values which are not present in any of the others arguments. */ PHP_FUNCTION(array_diff) { - php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_INTERNAL); + zval *args; + int argc, i; + uint32_t num; + HashTable exclude; + zval *value; + zend_string *str, *key; + zend_long idx; + zval dummy; + + if (ZEND_NUM_ARGS() < 2) { + php_error_docref(NULL, E_WARNING, "at least 2 parameters are required, %d given", ZEND_NUM_ARGS()); + return; + } + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { + return; + } + + if (Z_TYPE(args[0]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #1 is not an array"); + RETURN_NULL(); + } + + /* count number of elements */ + num = 0; + for (i = 1; i < argc; i++) { + if (Z_TYPE(args[i]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d is not an array", i + 1); + RETURN_NULL(); + } + num += zend_hash_num_elements(Z_ARRVAL(args[i])); + } + + if (num == 0) { + ZVAL_COPY(return_value, &args[0]); + return; + } + + ZVAL_NULL(&dummy); + /* create exclude map */ + zend_hash_init(&exclude, num, NULL, NULL, 0); + for (i = 1; i < argc; i++) { + ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[i]), value) { + str = zval_get_string(value); + zend_hash_add(&exclude, str, &dummy); + zend_string_release(str); + } ZEND_HASH_FOREACH_END(); + } + + /* copy all elements of first array that are not in exclude set */ + array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0]))); + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(args[0]), idx, key, value) { + str = zval_get_string(value); + if (!zend_hash_exists(&exclude, str)) { + if (key) { + value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value); + } else { + value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value); + } + zval_add_ref(value); + } + zend_string_release(str); + } ZEND_HASH_FOREACH_END(); + + zend_hash_destroy(&exclude); } /* }}} */ @@ -3654,44 +4727,50 @@ PHP_FUNCTION(array_udiff_uassoc) #define MULTISORT_TYPE 1 #define MULTISORT_LAST 2 -PHPAPI int php_multisort_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ +PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */ { - Bucket **ab = *(Bucket ***)a; - Bucket **bb = *(Bucket ***)b; + Bucket *ab = *(Bucket **)a; + Bucket *bb = *(Bucket **)b; int r; - int result = 0; - zval temp; + zend_long result; r = 0; do { - php_set_compare_func(ARRAYG(multisort_flags)[MULTISORT_TYPE][r] TSRMLS_CC); - - ARRAYG(compare_func)(&temp, *((zval **)ab[r]->pData), *((zval **)bb[r]->pData) TSRMLS_CC); - result = ARRAYG(multisort_flags)[MULTISORT_ORDER][r] * Z_LVAL(temp); + result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]); if (result != 0) { - return result; + return result > 0 ? 1 : -1; } r++; - } while (ab[r] != NULL); + } while (Z_TYPE(ab[r].val) != IS_UNDEF); - return result; + return 0; } /* }}} */ -#define MULTISORT_ABORT \ - for (k = 0; k < MULTISORT_LAST; k++) \ - efree(ARRAYG(multisort_flags)[k]); \ - efree(arrays); \ - efree(args); \ +#define MULTISORT_ABORT \ + efree(ARRAYG(multisort_func)); \ + efree(arrays); \ RETURN_FALSE; -/* {{{ proto bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]], ...]) +static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ { + Bucket *t; + Bucket **f = (Bucket **)p; + Bucket **g = (Bucket **)q; + + t = *f; + *f = *g; + *g = t; +} +/* }}} */ + +/* {{{ proto bool array_multisort(array &$array1 [, mixed $array1_sort_order [, mixed $array1_sort_flags [, mixed ... ]]] Sort multiple arrays at once similar to how ORDER BY clause works in SQL */ PHP_FUNCTION(array_multisort) { - zval*** args; - zval*** arrays; - Bucket*** indirect; + zval* args; + zval** arrays; + Bucket** indirect; + uint idx; Bucket* p; HashTable* hash; int argc; @@ -3700,18 +4779,18 @@ PHP_FUNCTION(array_multisort) int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */ int sort_order = PHP_SORT_ASC; int sort_type = PHP_SORT_REGULAR; - int i, k; + int i, k, n; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) { return; } /* Allocate space for storing pointers to input arrays and sort flags. */ - arrays = (zval ***)ecalloc(argc, sizeof(zval **)); + arrays = (zval **)ecalloc(argc, sizeof(zval *)); for (i = 0; i < MULTISORT_LAST; i++) { parse_state[i] = 0; - ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int)); } + ARRAYG(multisort_func) = (compare_func_t*)ecalloc(argc, sizeof(compare_func_t)); /* Here we go through the input arguments and parse them. Each one can * be either an array or a sort flag which follows an array. If not @@ -3719,32 +4798,35 @@ PHP_FUNCTION(array_multisort) * accordingly. There can't be two sort flags of the same type after an * array, and the very first argument has to be an array. */ for (i = 0; i < argc; i++) { - if (Z_TYPE_PP(args[i]) == IS_ARRAY) { + zval *arg = &args[i]; + + ZVAL_DEREF(arg); + if (Z_TYPE_P(arg) == IS_ARRAY) { + SEPARATE_ARRAY(arg); /* We see the next array, so we update the sort flags of * the previous array and reset the sort flags. */ if (i > 0) { - ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order; - ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type; + ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC); sort_order = PHP_SORT_ASC; sort_type = PHP_SORT_REGULAR; } - arrays[num_arrays++] = args[i]; + arrays[num_arrays++] = arg; /* Next one may be an array or a list of sort flags. */ for (k = 0; k < MULTISORT_LAST; k++) { parse_state[k] = 1; } - } else if (Z_TYPE_PP(args[i]) == IS_LONG) { - switch (Z_LVAL_PP(args[i]) & ~PHP_SORT_FLAG_CASE) { + } else if (Z_TYPE_P(arg) == IS_LONG) { + switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) { case PHP_SORT_ASC: case PHP_SORT_DESC: /* flag allowed here */ if (parse_state[MULTISORT_ORDER] == 1) { /* Save the flag and make sure then next arg is not the current flag. */ - sort_order = Z_LVAL_PP(args[i]) == PHP_SORT_DESC ? -1 : 1; + sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC; parse_state[MULTISORT_ORDER] = 0; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); + php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); MULTISORT_ABORT; } break; @@ -3759,45 +4841,41 @@ PHP_FUNCTION(array_multisort) /* flag allowed here */ if (parse_state[MULTISORT_TYPE] == 1) { /* Save the flag and make sure then next arg is not the current flag. */ - sort_type = Z_LVAL_PP(args[i]); + sort_type = (int)Z_LVAL_P(arg); parse_state[MULTISORT_TYPE] = 0; } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); + php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1); MULTISORT_ABORT; } break; default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1); + php_error_docref(NULL, E_WARNING, "Argument #%d is an unknown sort flag", i + 1); MULTISORT_ABORT; break; } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1); + php_error_docref(NULL, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1); MULTISORT_ABORT; } } /* Take care of the last array sort flags. */ - ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order; - ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type; + ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC); /* Make sure the arrays are of the same size. */ - array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0])); + array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0])); for (i = 0; i < num_arrays; i++) { - if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent"); + if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != array_size) { + php_error_docref(NULL, E_WARNING, "Array sizes are inconsistent"); MULTISORT_ABORT; } } /* If all arrays are empty we don't need to do anything. */ if (array_size < 1) { - for (k = 0; k < MULTISORT_LAST; k++) { - efree(ARRAYG(multisort_flags)[k]); - } + efree(ARRAYG(multisort_func)); efree(arrays); - efree(args); RETURN_TRUE; } @@ -3805,41 +4883,50 @@ PHP_FUNCTION(array_multisort) * M is the number of entries in each input array and N is the number * of the input arrays + 1. The last column is NULL to indicate the end * of the row. */ - indirect = (Bucket ***)safe_emalloc(array_size, sizeof(Bucket **), 0); + indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0); for (i = 0; i < array_size; i++) { - indirect[i] = (Bucket **)safe_emalloc((num_arrays + 1), sizeof(Bucket *), 0); + indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0); } for (i = 0; i < num_arrays; i++) { k = 0; - for (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) { - indirect[k][i] = p; + for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) { + p = Z_ARRVAL_P(arrays[i])->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + indirect[k][i] = *p; + k++; } } for (k = 0; k < array_size; k++) { - indirect[k][num_arrays] = NULL; + ZVAL_UNDEF(&indirect[k][num_arrays].val); } /* Do the actual sort magic - bada-bim, bada-boom. */ - zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC); + zend_qsort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp); /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */ HANDLE_BLOCK_INTERRUPTIONS(); for (i = 0; i < num_arrays; i++) { - hash = Z_ARRVAL_PP(arrays[i]); - hash->pListHead = indirect[0][i];; - hash->pListTail = NULL; - hash->pInternalPointer = hash->pListHead; - - for (k = 0; k < array_size; k++) { - if (hash->pListTail) { - hash->pListTail->pListNext = indirect[k][i]; + int repack; + + hash = Z_ARRVAL_P(arrays[i]); + hash->nNumUsed = array_size; + hash->nInternalPointer = 0; + repack = !(hash->u.flags & HASH_FLAG_PACKED); + + for (n = 0, k = 0; k < array_size; k++) { + hash->arData[k] = indirect[k][i]; + if (hash->arData[k].key == NULL) { + hash->arData[k].h = n++; + } else { + repack = 0; } - indirect[k][i]->pListLast = hash->pListTail; - indirect[k][i]->pListNext = NULL; - hash->pListTail = indirect[k][i]; } - - zend_hash_reindex(hash, 1); + hash->nNextFreeElement = array_size; + if (repack) { + zend_hash_to_packed(hash); + } else { + zend_hash_rehash(hash); + } } HANDLE_UNBLOCK_INTERRUPTIONS(); @@ -3848,11 +4935,8 @@ PHP_FUNCTION(array_multisort) efree(indirect[i]); } efree(indirect); - for (k = 0; k < MULTISORT_LAST; k++) { - efree(ARRAYG(multisort_flags)[k]); - } + efree(ARRAYG(multisort_func)); efree(arrays); - efree(args); RETURN_TRUE; } /* }}} */ @@ -3862,14 +4946,12 @@ PHP_FUNCTION(array_multisort) PHP_FUNCTION(array_rand) { zval *input; - long randval, num_req = 1; - int num_avail, key_type; - char *string_key; - uint string_key_len; - ulong num_key; - HashPosition pos; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|l", &input, &num_req) == FAILURE) { + zend_long randval, num_req = 1; + int num_avail; + zend_string *string_key; + zend_ulong num_key; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) { return; } @@ -3877,34 +4959,36 @@ PHP_FUNCTION(array_rand) if (ZEND_NUM_ARGS() > 1) { if (num_req <= 0 || num_req > num_avail) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argument has to be between 1 and the number of elements in the array"); + php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array"); return; } } /* Make the return value an array only if we need to pass back more than one result. */ if (num_req > 1) { - array_init_size(return_value, num_req); + array_init_size(return_value, (uint32_t)num_req); } /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */ - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); - while (num_req && (key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 0, &pos)) != HASH_KEY_NON_EXISTENT) { + ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) { + if (!num_req) { + break; + } - randval = php_rand(TSRMLS_C); + randval = php_rand(); if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) { /* If we are returning a single result, just do it. */ if (Z_TYPE_P(return_value) != IS_ARRAY) { - if (key_type == HASH_KEY_IS_STRING) { - RETURN_STRINGL(string_key, string_key_len - 1, 1); + if (string_key) { + RETURN_STR_COPY(string_key); } else { RETURN_LONG(num_key); } } else { /* Append the result to the return value. */ - if (key_type == HASH_KEY_IS_STRING) { - add_next_index_stringl(return_value, string_key, string_key_len - 1, 1); + if (string_key) { + add_next_index_str(return_value, zend_string_copy(string_key)); } else { add_next_index_long(return_value, num_key); } @@ -3912,8 +4996,7 @@ PHP_FUNCTION(array_rand) num_req--; } num_avail--; - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); - } + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -3922,28 +5005,23 @@ PHP_FUNCTION(array_rand) PHP_FUNCTION(array_sum) { zval *input, - **entry, + *entry, entry_n; - HashPosition pos; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) { return; } ZVAL_LONG(return_value, 0); - for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); - zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS; - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos) - ) { - if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) { + if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) { continue; } - entry_n = **entry; - zval_copy_ctor(&entry_n); - convert_scalar_to_number(&entry_n TSRMLS_CC); - fast_add_function(return_value, return_value, &entry_n TSRMLS_CC); - } + ZVAL_COPY(&entry_n, entry); + convert_scalar_to_number(&entry_n); + fast_add_function(return_value, return_value, &entry_n); + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -3952,12 +5030,11 @@ PHP_FUNCTION(array_sum) PHP_FUNCTION(array_product) { zval *input, - **entry, + *entry, entry_n; - HashPosition pos; double dval; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &input) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &input) == FAILURE) { return; } @@ -3966,20 +5043,16 @@ PHP_FUNCTION(array_product) return; } - for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); - zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void **)&entry, &pos) == SUCCESS; - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos) - ) { - if (Z_TYPE_PP(entry) == IS_ARRAY || Z_TYPE_PP(entry) == IS_OBJECT) { + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) { + if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) { continue; } - entry_n = **entry; - zval_copy_ctor(&entry_n); - convert_scalar_to_number(&entry_n TSRMLS_CC); + ZVAL_COPY(&entry_n, entry); + convert_scalar_to_number(&entry_n); if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) { dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n); - if ( (double)LONG_MIN <= dval && dval <= (double)LONG_MAX ) { + if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) { Z_LVAL_P(return_value) *= Z_LVAL(entry_n); continue; } @@ -3987,7 +5060,7 @@ PHP_FUNCTION(array_product) convert_to_double(return_value); convert_to_double(&entry_n); Z_DVAL_P(return_value) *= Z_DVAL(entry_n); - } + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -3996,26 +5069,24 @@ PHP_FUNCTION(array_product) PHP_FUNCTION(array_reduce) { zval *input; - zval **args[2]; - zval **operand; - zval *result = NULL; - zval *retval; + zval args[2]; + zval *operand; + zval result; + zval retval; zend_fcall_info fci; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; zval *initial = NULL; - HashPosition pos; HashTable *htbl; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "af|z", &input, &fci, &fci_cache, &initial) == FAILURE) { return; } + if (ZEND_NUM_ARGS() > 2) { - ALLOC_ZVAL(result); - MAKE_COPY_ZVAL(&initial, result); + ZVAL_DUP(&result, initial); } else { - MAKE_STD_ZVAL(result); - ZVAL_NULL(result); + ZVAL_NULL(&result); } /* (zval **)input points to an element of argument stack @@ -4024,38 +5095,32 @@ PHP_FUNCTION(array_reduce) htbl = Z_ARRVAL_P(input); if (zend_hash_num_elements(htbl) == 0) { - if (result) { - RETVAL_ZVAL(result, 1, 1); - } + ZVAL_COPY_VALUE(return_value, &result); return; } - fci.retval_ptr_ptr = &retval; + fci.retval = &retval; fci.param_count = 2; fci.no_separation = 0; - zend_hash_internal_pointer_reset_ex(htbl, &pos); - while (zend_hash_get_current_data_ex(htbl, (void **)&operand, &pos) == SUCCESS) { - - if (result) { - args[0] = &result; - args[1] = operand; - fci.params = args; + ZEND_HASH_FOREACH_VAL(htbl, operand) { + ZVAL_COPY(&args[0], &result); + ZVAL_COPY(&args[1], operand); + fci.params = args; - if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) { - zval_ptr_dtor(&result); - result = retval; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the reduction callback"); - return; - } + if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&result); + ZVAL_COPY_VALUE(&result, &retval); } else { - result = *operand; - zval_add_ref(&result); + zval_ptr_dtor(&args[1]); + zval_ptr_dtor(&args[0]); + return; } - zend_hash_move_forward_ex(htbl, &pos); - } - RETVAL_ZVAL(result, 1, 1); + } ZEND_HASH_FOREACH_END(); + + RETVAL_ZVAL(&result, 1, 1); } /* }}} */ @@ -4064,20 +5129,18 @@ PHP_FUNCTION(array_reduce) PHP_FUNCTION(array_filter) { zval *array; - zval **operand; - zval **args[2]; - zval *retval = NULL; - zval *key = NULL; + zval *operand; + zval *key; + zval args[2]; + zval retval; zend_bool have_callback = 0; - long use_type = 0; - char *string_key; + zend_long use_type = 0; + zend_string *string_key; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; - uint string_key_len; - ulong num_key; - HashPosition pos; + zend_ulong num_key; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) { return; } @@ -4089,75 +5152,64 @@ PHP_FUNCTION(array_filter) if (ZEND_NUM_ARGS() > 1) { have_callback = 1; fci.no_separation = 0; - fci.retval_ptr_ptr = &retval; - + fci.retval = &retval; if (use_type == ARRAY_FILTER_USE_BOTH) { fci.param_count = 2; - args[1] = &key; + key = &args[1]; } else { fci.param_count = 1; - if (use_type == ARRAY_FILTER_USE_KEY) { - args[0] = &key; - } + key = &args[0]; } } - 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); - + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_key, string_key, operand) { if (have_callback) { 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 (!string_key) { + ZVAL_LONG(key, num_key); + } else { + ZVAL_STR_COPY(key, string_key); } } - if (use_type != ARRAY_FILTER_USE_KEY) { - args[0] = operand; + ZVAL_COPY(&args[0], operand); } fci.params = args; - if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) { - int retval_true = zend_is_true(retval); - - zval_ptr_dtor(&retval); - if (use_type) { - zval_ptr_dtor(&key); + if (zend_call_function(&fci, &fci_cache) == SUCCESS) { + zval_ptr_dtor(&args[0]); + if (use_type == ARRAY_FILTER_USE_BOTH) { + zval_ptr_dtor(&args[1]); } - if (!retval_true) { + if (!Z_ISUNDEF(retval)) { + int retval_true = zend_is_true(&retval); + + zval_ptr_dtor(&retval); + if (!retval_true) { + continue; + } + } else { continue; } } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback"); + zval_ptr_dtor(&args[0]); + if (use_type == ARRAY_FILTER_USE_BOTH) { + zval_ptr_dtor(&args[1]); + } return; } - } else if (!zend_is_true(*operand)) { + } else if (!zend_is_true(operand)) { continue; } - zval_add_ref(operand); - 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; - - case HASH_KEY_IS_LONG: - zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand, sizeof(zval *), NULL); - break; + if (string_key) { + operand = zend_hash_update(Z_ARRVAL_P(return_value), string_key, operand); + } else { + operand = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, operand); } - } + zval_add_ref(operand); + } ZEND_HASH_FOREACH_END(); } /* }}} */ @@ -4165,131 +5217,162 @@ PHP_FUNCTION(array_filter) Applies the callback to the elements in given arrays. */ PHP_FUNCTION(array_map) { - zval ***arrays = NULL; + zval *arrays = NULL; int n_arrays = 0; - zval ***params; - zval *result, *null; - HashPosition *array_pos; - zval **args; + zval result; 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; + int i; + uint32_t k, maxlen = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0) + Z_PARAM_VARIADIC('+', arrays, n_arrays) + ZEND_PARSE_PARAMETERS_END(); +#endif 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_PP(arrays[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2); - efree(arrays); - efree(args); - efree(array_len); - efree(array_pos); + if (n_arrays == 1) { + zend_ulong num_key; + zend_string *str_key; + zval *zv, arg; + + if (Z_TYPE(arrays[0]) != IS_ARRAY) { + php_error_docref(NULL, 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_PP(arrays[i])); - if (array_len[i] > maxlen) { - maxlen = array_len[i]; - } - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(arrays[i]), &array_pos[i]); - } + maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0])); - efree(arrays); - - /* 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; - } + /* Short-circuit: if no callback and only one array, just return it. */ + if (!ZEND_FCI_INITIALIZED(fci)) { + ZVAL_COPY(return_value, &arrays[0]); + return; + } - array_init_size(return_value, maxlen); - params = (zval ***)safe_emalloc(n_arrays, sizeof(zval **), 0); - MAKE_STD_ZVAL(null); - ZVAL_NULL(null); + array_init_size(return_value, maxlen); - /* We iterate through all the arrays at once. */ - for (k = 0; k < maxlen; k++) { - uint str_key_len; - ulong num_key; - char *str_key; - int key_type = 0; + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) { + fci.retval = &result; + fci.param_count = 1; + fci.params = &arg; + fci.no_separation = 0; - /* If no callback, the result will be an array, consisting of current - * entries from all arrays. */ - if (!ZEND_FCI_INITIALIZED(fci)) { - MAKE_STD_ZVAL(result); - array_init_size(result, n_arrays); - } + ZVAL_COPY(&arg, zv); - 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]) { - zend_hash_get_current_data_ex(Z_ARRVAL_P(args[i]), (void **)¶ms[i], &array_pos[i]); - - /* 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, &str_key_len, &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) != SUCCESS || Z_TYPE(result) == IS_UNDEF) { + zval_dtor(return_value); + zval_ptr_dtor(&arg); + RETURN_NULL(); } else { - params[i] = &null; + zval_ptr_dtor(&arg); } - - if (!ZEND_FCI_INITIALIZED(fci)) { - zval_add_ref(params[i]); - add_next_index_zval(result, *params[i]); + if (str_key) { + zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, &result); + } else { + zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result); } - } - - if (ZEND_FCI_INITIALIZED(fci)) { - fci.retval_ptr_ptr = &result; - fci.param_count = n_arrays; - fci.params = params; - fci.no_separation = 0; + } ZEND_HASH_FOREACH_END(); + } else { + uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition)); - if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || !result) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback"); - efree(array_len); - efree(args); + for (i = 0; i < n_arrays; i++) { + if (Z_TYPE(arrays[i]) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, "Argument #%d should be an array", i + 2); efree(array_pos); - zval_dtor(return_value); - zval_ptr_dtor(&null); - efree(params); - RETURN_NULL(); + return; + } + if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) { + maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i])); } } - if (n_arrays > 1) { - add_next_index_zval(return_value, result); + 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. */ + uint32_t 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 { - if (key_type == HASH_KEY_IS_STRING) { - add_assoc_zval_ex(return_value, str_key, str_key_len, result); - } else { - add_index_zval(return_value, num_key, result); + 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. */ + uint32_t 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) != SUCCESS || Z_TYPE(result) == IS_UNDEF) { + 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); } - - zval_ptr_dtor(&null); - efree(params); - efree(array_len); - efree(array_pos); - efree(args); } /* }}} */ @@ -4300,13 +5383,20 @@ PHP_FUNCTION(array_key_exists) zval *key; /* key to check for */ HashTable *array; /* array to check in */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zH", &key, &array) == FAILURE) { +#ifndef FAST_ZPP + if (zend_parse_parameters(ZEND_NUM_ARGS(), "zH", &key, &array) == FAILURE) { return; } +#else + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(key) + Z_PARAM_ARRAY_OR_OBJECT_HT(array) + ZEND_PARSE_PARAMETERS_END(); +#endif switch (Z_TYPE_P(key)) { case IS_STRING: - if (zend_symtable_exists(array, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1)) { + if (zend_symtable_exists(array, Z_STR_P(key))) { RETURN_TRUE; } RETURN_FALSE; @@ -4316,13 +5406,13 @@ PHP_FUNCTION(array_key_exists) } RETURN_FALSE; case IS_NULL: - if (zend_hash_exists(array, "", 1)) { + if (zend_hash_exists(array, ZSTR_EMPTY_ALLOC())) { RETURN_TRUE; } RETURN_FALSE; default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "The first argument should be either a string or an integer"); + php_error_docref(NULL, E_WARNING, "The first argument should be either a string or an integer"); RETURN_FALSE; } } @@ -4332,23 +5422,21 @@ PHP_FUNCTION(array_key_exists) Split array into chunks */ PHP_FUNCTION(array_chunk) { - int argc = ZEND_NUM_ARGS(), key_type, num_in; - long size, current = 0; - char *str_key; - uint str_key_len; - ulong num_key; + int argc = ZEND_NUM_ARGS(), num_in; + zend_long size, current = 0; + zend_string *str_key; + zend_ulong num_key; zend_bool preserve_keys = 0; zval *input = NULL; - zval *chunk = NULL; - zval **entry; - HashPosition pos; + zval chunk; + zval *entry; - if (zend_parse_parameters(argc TSRMLS_CC, "al|b", &input, &size, &preserve_keys) == FAILURE) { + if (zend_parse_parameters(argc, "al|b", &input, &size, &preserve_keys) == FAILURE) { return; } /* Do bounds checking for size parameter. */ if (size < 1) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Size parameter expected to be greater than 0"); + php_error_docref(NULL, E_WARNING, "Size parameter expected to be greater than 0"); return; } @@ -4358,46 +5446,39 @@ PHP_FUNCTION(array_chunk) size = num_in > 0 ? num_in : 1; } - array_init_size(return_value, ((num_in - 1) / size) + 1); + array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1)); - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(input), &pos); - while (zend_hash_get_current_data_ex(Z_ARRVAL_P(input), (void**)&entry, &pos) == SUCCESS) { + ZVAL_UNDEF(&chunk); + + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) { /* If new chunk, create and initialize it. */ - if (!chunk) { - MAKE_STD_ZVAL(chunk); - array_init_size(chunk, size); + if (Z_TYPE(chunk) == IS_UNDEF) { + array_init_size(&chunk, (uint32_t)size); } /* Add entry to the chunk, preserving keys if necessary. */ - zval_add_ref(entry); - if (preserve_keys) { - key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &str_key, &str_key_len, &num_key, 0, &pos); - switch (key_type) { - case HASH_KEY_IS_STRING: - add_assoc_zval_ex(chunk, str_key, str_key_len, *entry); - break; - default: - add_index_zval(chunk, num_key, *entry); - break; + if (str_key) { + entry = zend_hash_update(Z_ARRVAL(chunk), str_key, entry); + } else { + entry = zend_hash_index_update(Z_ARRVAL(chunk), num_key, entry); } } else { - add_next_index_zval(chunk, *entry); + entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry); } + zval_add_ref(entry); /* If reached the chunk size, add it to the result array, and reset the * pointer. */ if (!(++current % size)) { - add_next_index_zval(return_value, chunk); - chunk = NULL; + add_next_index_zval(return_value, &chunk); + ZVAL_UNDEF(&chunk); } - - zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); - } + } ZEND_HASH_FOREACH_END(); /* Add the final chunk if there is one. */ - if (chunk) { - add_next_index_zval(return_value, chunk); + if (Z_TYPE(chunk) != IS_UNDEF) { + add_next_index_zval(return_value, &chunk); } } /* }}} */ @@ -4406,20 +5487,20 @@ PHP_FUNCTION(array_chunk) Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */ PHP_FUNCTION(array_combine) { - zval *values, *keys; - HashPosition pos_values, pos_keys; - zval **entry_keys, **entry_values; + HashTable *values, *keys; + uint32_t pos_values = 0; + zval *entry_keys, *entry_values; int num_keys, num_values; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &keys, &values) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "hh", &keys, &values) == FAILURE) { return; } - num_keys = zend_hash_num_elements(Z_ARRVAL_P(keys)); - num_values = zend_hash_num_elements(Z_ARRVAL_P(values)); + num_keys = zend_hash_num_elements(keys); + num_values = zend_hash_num_elements(values); if (num_keys != num_values) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Both parameters should have an equal number of elements"); + php_error_docref(NULL, E_WARNING, "Both parameters should have an equal number of elements"); RETURN_FALSE; } @@ -4429,35 +5510,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 (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&entry_keys, &pos_keys) == SUCCESS && - zend_hash_get_current_data_ex(Z_ARRVAL_P(values), (void **)&entry_values, &pos_values) == SUCCESS - ) { - if (Z_TYPE_PP(entry_keys) == IS_LONG) { - zval_add_ref(entry_values); - add_index_zval(return_value, Z_LVAL_PP(entry_keys), *entry_values); - } else { - zval key, *key_ptr = *entry_keys; - - if (Z_TYPE_PP(entry_keys) != IS_STRING) { - key = **entry_keys; - zval_copy_ctor(&key); - convert_to_string(&key); - key_ptr = &key; - } - - zval_add_ref(entry_values); - add_assoc_zval_ex(return_value, Z_STRVAL_P(key_ptr), Z_STRLEN_P(key_ptr) + 1, *entry_values); - - if (key_ptr != *entry_keys) { - zval_dtor(&key); + ZEND_HASH_FOREACH_VAL(keys, entry_keys) { + while (1) { + if (pos_values >= values->nNumUsed) { + break; + } else if (Z_TYPE(values->arData[pos_values].val) != IS_UNDEF) { + entry_values = &values->arData[pos_values].val; + if (Z_TYPE_P(entry_keys) == IS_LONG) { + entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value), + Z_LVAL_P(entry_keys), entry_values); + } else { + zend_string *key = zval_get_string(entry_keys); + entry_values = zend_symtable_update(Z_ARRVAL_P(return_value), + key, entry_values); + zend_string_release(key); + } + zval_add_ref(entry_values); + 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(); } /* }}} */ |
