From 751f0ef85c86e15e34341154363ce2aeffc123dd Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Wed, 18 Jul 2018 18:03:19 -0700 Subject: [PATCH] [Proposal] Reduce the minimum size for packed arrays from 8 to 2 Continue to require that associative arrays have a minimum size of 8 and are powers of 8. This can reduce the memory usage of some applications by ~16% in applications that heavily use small arrays. Stop requiring that packed arrays have a size that is a power of 2. There's no reason to for packed arrays, since php isn't doing any hash lookups with bit operations. Add a helper to create an associative array with a minimum size. This may help with clarity/avoiding unnecessary size recalculations. (performance impact may be negligible) https://nikic.github.io/2014/12/22/PHPs-new-hashtable-implementation.html gives some background information on the `HashTable` implementation on master. This is a followup to https://github.com/php/php-src/pull/4753#issuecomment-536312127 (I didn't see any other PRs mentioning HT_MIN_SIZE) --- Zend/zend_API.c | 2 +- Zend/zend_API.h | 6 ++-- Zend/zend_ast.c | 8 ++++-- Zend/zend_builtin_functions.c | 2 +- Zend/zend_execute.c | 2 +- Zend/zend_hash.c | 50 +++++++++++++++++++++++++++------ Zend/zend_hash.h | 14 +++++++++ Zend/zend_operators.c | 2 +- Zend/zend_types.h | 15 ++++++++-- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 8 +++--- ext/json/json_parser.y | 2 +- ext/opcache/zend_persist.c | 3 +- ext/opcache/zend_persist_calc.c | 2 +- ext/standard/array.c | 6 ++-- ext/standard/basic_functions.c | 4 +-- ext/standard/datetime.c | 2 +- ext/standard/dir.c | 2 +- ext/standard/file.c | 2 +- ext/standard/scanf.c | 2 +- ext/standard/streamsfuncs.c | 2 +- ext/tokenizer/tokenizer.c | 2 +- 22 files changed, 100 insertions(+), 40 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 14075847ad1f5..5cb5105cb9bc5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3299,7 +3299,7 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam if (zend_is_callable_ex(callable, NULL, 0, callable_name, &fcc, NULL)) { if (Z_TYPE_P(callable) == IS_STRING && fcc.calling_scope) { zval_ptr_dtor_str(callable); - array_init(callable); + array_init_size(callable, 2); add_next_index_str(callable, zend_string_copy(fcc.calling_scope->name)); add_next_index_str(callable, zend_string_copy(fcc.function_handler->common.function_name)); } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index ae0363f7657bc..40d5dac65061d 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -416,8 +416,10 @@ ZEND_API const char *zend_get_type_by_const(int type); #define DLEXPORT #endif -#define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) -#define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) +#define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) +#define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) +#define array_init_assoc(arg) ZVAL_ARR((arg), zend_new_array_assoc(0)) +#define array_init_assoc_size(arg, size) ZVAL_ARR((arg), zend_new_array_assoc(size)) ZEND_API int object_init(zval *arg); ZEND_API int object_init_ex(zval *arg, zend_class_entry *ce); ZEND_API int object_and_properties_init(zval *arg, zend_class_entry *ce, HashTable *properties); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 5d7c98dad5b7c..edd68b27ef181 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -692,13 +692,15 @@ ZEND_API int ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_c { uint32_t i; zend_ast_list *list = zend_ast_get_list(ast); + uint32_t n_children = list->children; - if (!list->children) { + if (!n_children) { ZVAL_EMPTY_ARRAY(result); break; } - array_init(result); - for (i = 0; i < list->children; i++) { + /** Usually, there won't be an AST_UNPACK or duplicate keys. Assume that's the initial capacity. */ + array_init_size(result, n_children); + for (i = 0; i < n_children; i++) { zend_ast *elem = list->child[i]; if (elem->kind == ZEND_AST_UNPACK) { if (UNEXPECTED(zend_ast_evaluate(&op1, elem->child[0], scope) != SUCCESS)) { diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 881624d321a87..1a87576bc0c3b 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -145,7 +145,7 @@ ZEND_FUNCTION(gc_status) zend_gc_get_status(&status); - array_init_size(return_value, 3); + array_init_assoc_size(return_value, 4); add_assoc_long_ex(return_value, "runs", sizeof("runs")-1, (long)status.runs); add_assoc_long_ex(return_value, "collected", sizeof("collected")-1, (long)status.collected); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 5d619437f5eb6..d4ed777c5b29b 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2165,7 +2165,7 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * return; } } - array_init(container); + array_init(container); /* TODO benchmark and check if array_init_assoc is faster */ goto fetch_from_array; } else { goto return_null; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 1723833b1fad2..aa2f67e99ea17 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -166,10 +166,18 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht) uint32_t nSize = ht->nTableSize; if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) { + if (nSize < HT_MIN_SIZE_UNPACKED) { + nSize = HT_MIN_SIZE_UNPACKED; + ht->nTableSize = HT_MIN_SIZE_UNPACKED; + } data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1); - } else if (EXPECTED(nSize == HT_MIN_SIZE)) { - data = emalloc(HT_SIZE_EX(HT_MIN_SIZE, HT_SIZE_TO_MASK(HT_MIN_SIZE))); - ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE); + } else if (EXPECTED(nSize <= HT_MIN_SIZE_UNPACKED)) { + if (nSize < HT_MIN_SIZE_UNPACKED) { + nSize = HT_MIN_SIZE_UNPACKED; + ht->nTableSize = HT_MIN_SIZE_UNPACKED; + } + data = emalloc(HT_SIZE_EX(HT_MIN_SIZE_UNPACKED, HT_SIZE_TO_MASK(HT_MIN_SIZE_UNPACKED))); + ht->nTableMask = HT_SIZE_TO_MASK(HT_MIN_SIZE_UNPACKED); HT_SET_DATA_ADDR(ht, data); /* Don't overwrite iterator count. */ ht->u.v.flags = HASH_FLAG_STATIC_KEYS; @@ -232,6 +240,7 @@ static zend_always_inline void zend_hash_real_init_ex(HashTable *ht, int packed) static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; +/* XXX: Is MIN_SIZE_UNPACKED the best choice vs this (8 vs 2)? Haven't benchmarked it. */ ZEND_API const HashTable zend_empty_array = { .gc.refcount = 2, .gc.u.type_info = IS_ARRAY | (GC_IMMUTABLE << GC_FLAGS_SHIFT), @@ -240,7 +249,7 @@ ZEND_API const HashTable zend_empty_array = { .arData = (Bucket*)&uninitialized_bucket[2], .nNumUsed = 0, .nNumOfElements = 0, - .nTableSize = HT_MIN_SIZE, + .nTableSize = HT_MIN_SIZE_UNPACKED, .nInternalPointer = 0, .nNextFreeElement = 0, .pDestructor = ZVAL_PTR_DTOR @@ -258,7 +267,8 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize ht->nInternalPointer = 0; ht->nNextFreeElement = ZEND_LONG_MIN; ht->pDestructor = pDestructor; - ht->nTableSize = zend_hash_check_size(nSize); + /* TODO: Decide on the best way to specify the size of a packed table exactly? This is a first attempt*/ + ht->nTableSize = (nSize <= HT_MIN_SIZE_UNPACKED ? (nSize < HT_MIN_SIZE ? HT_MIN_SIZE : nSize) : zend_hash_check_size(nSize)); } ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent) @@ -273,6 +283,13 @@ ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void) return ht; } +ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_assoc_0(void) +{ + HashTable *ht = emalloc(sizeof(HashTable)); + _zend_hash_init_int(ht, HT_MIN_SIZE_UNPACKED, ZVAL_PTR_DTOR, 0); + return ht; +} + ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize) { HashTable *ht = emalloc(sizeof(HashTable)); @@ -280,10 +297,18 @@ ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t nSize) return ht; } +ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_assoc(uint32_t nSize) +{ + HashTable *ht = emalloc(sizeof(HashTable)); + _zend_hash_init_int(ht, nSize < HT_MIN_SIZE_UNPACKED ? HT_MIN_SIZE_UNPACKED : nSize, ZVAL_PTR_DTOR, 0); + return ht; +} + ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2) { Bucket *p; HashTable *ht = emalloc(sizeof(HashTable)); + /* XXX: Currently, HT_MIN_SIZE == 2. will need to adjust all calls like this if HT_MIN_SIZE goes below 2 */ _zend_hash_init_int(ht, HT_MIN_SIZE, ZVAL_PTR_DTOR, 0); ht->nNumUsed = ht->nNumOfElements = ht->nNextFreeElement = 2; zend_hash_real_init_packed_ex(ht); @@ -339,11 +364,18 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht) void *new_data, *old_data = HT_GET_DATA_ADDR(ht); Bucket *old_buckets = ht->arData; uint32_t nSize = ht->nTableSize; + if (nSize < HT_MIN_SIZE_UNPACKED) { + nSize = HT_MIN_SIZE_UNPACKED; + ht->nTableSize = HT_MIN_SIZE_UNPACKED; + } else if (nSize & (nSize - 1)) { + nSize = zend_hash_check_size(nSize); + ht->nTableSize = nSize; + } HT_ASSERT_RC1(ht); HT_FLAGS(ht) &= ~HASH_FLAG_PACKED; new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); - ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize); + ht->nTableMask = HT_SIZE_TO_MASK(nSize); HT_SET_DATA_ADDR(ht, new_data); memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed); pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); @@ -1177,14 +1209,14 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht) if (ht->nNumUsed > ht->nNumOfElements + (ht->nNumOfElements >> 5)) { /* additional term is there to amortize the cost of compaction */ zend_hash_rehash(ht); - } else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */ + } else if (EXPECTED(ht->nTableSize < HT_MAX_SIZE)) { /* Let's double the table size */ void *new_data, *old_data = HT_GET_DATA_ADDR(ht); - uint32_t nSize = ht->nTableSize + ht->nTableSize; + uint32_t nSize = zend_hash_check_size(ht->nTableSize + ht->nTableSize); Bucket *old_buckets = ht->arData; ht->nTableSize = nSize; new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); - ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize); + ht->nTableMask = HT_SIZE_TO_MASK(nSize); HT_SET_DATA_ADDR(ht, new_data); memcpy(ht->arData, old_buckets, sizeof(Bucket) * ht->nNumUsed); pefree(old_data, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 294a9c2c26389..cfbb7419c3ff7 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -292,13 +292,27 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht); : \ _zend_new_array((size)) \ ) +# define zend_new_array_assoc(size) \ + (__builtin_constant_p(size) ? \ + ((((uint32_t)(size)) <= HT_MIN_SIZE_UNPACKED) ? \ + _zend_new_array_assoc_0() \ + : \ + _zend_new_array_assoc((size)) \ + ) \ + : \ + _zend_new_array_assoc((size)) \ + ) #else # define zend_new_array(size) \ _zend_new_array(size) +# define zend_new_array_assoc(size) \ + _zend_new_array_assoc(size) #endif ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_0(void); +ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_assoc_0(void); ZEND_API HashTable* ZEND_FASTCALL _zend_new_array(uint32_t size); +ZEND_API HashTable* ZEND_FASTCALL _zend_new_array_assoc(uint32_t size); ZEND_API HashTable* ZEND_FASTCALL zend_new_pair(zval *val1, zval *val2); ZEND_API uint32_t zend_array_count(HashTable *ht); ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 9fdc006e644c0..aaaed2788253a 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -712,7 +712,7 @@ ZEND_API void ZEND_FASTCALL convert_to_array(zval *op) /* {{{ */ } else { zval_ptr_dtor(op); /*ZVAL_EMPTY_ARRAY(op);*/ - array_init(op); + array_init_assoc(op); } } break; diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 7796979ed842f..e58abaa61903f 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -394,7 +394,8 @@ struct _zend_array { #define HT_INVALID_IDX ((uint32_t) -1) #define HT_MIN_MASK ((uint32_t) -2) -#define HT_MIN_SIZE 8 +#define HT_MIN_SIZE 2 +#define HT_MIN_SIZE_UNPACKED 8 #if SIZEOF_SIZE_T == 4 # define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */ @@ -422,7 +423,7 @@ struct _zend_array { HT_HASH_EX((ht)->arData, idx) #define HT_SIZE_TO_MASK(nTableSize) \ - ((uint32_t)(-((nTableSize) + (nTableSize)))) + (uint32_t)(-(zend_hash_check_size((nTableSize) + (nTableSize)))) #define HT_HASH_SIZE(nTableMask) \ (((size_t)(uint32_t)-(int32_t)(nTableMask)) * sizeof(uint32_t)) #define HT_DATA_SIZE(nTableSize) \ @@ -439,7 +440,15 @@ struct _zend_array { size_t size = HT_HASH_SIZE((ht)->nTableMask); \ __m128i xmm0 = _mm_setzero_si128(); \ xmm0 = _mm_cmpeq_epi8(xmm0, xmm0); \ - ZEND_ASSERT(size >= 64 && ((size & 0x3f) == 0)); \ + if (size < 64) { \ + ZEND_ASSERT(size == 16 || size == 32); \ + _mm_storeu_si128((__m128i*)p, xmm0); \ + if (size >= 32) { \ + _mm_storeu_si128((__m128i*)(p+16), xmm0); \ + } \ + break; \ + } \ + ZEND_ASSERT(((size & 0x3f) == 0)); \ do { \ _mm_storeu_si128((__m128i*)p, xmm0); \ _mm_storeu_si128((__m128i*)(p+16), xmm0); \ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 241c56851c6c8..c326110bd339e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5801,7 +5801,7 @@ ZEND_VM_COLD_CONST_HANDLER(51, ZEND_CAST, CONST|TMP|VAR|CV, ANY, TYPE) } Z_OBJ_P(result)->properties = ht; } else if (Z_TYPE_P(expr) != IS_NULL) { - Z_OBJ_P(result)->properties = ht = zend_new_array(1); + Z_OBJ_P(result)->properties = ht = zend_new_array_assoc(1); expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr); if (OP1_TYPE == IS_CONST) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c1975fd0b9ae1..ef66d1ede1536 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3966,7 +3966,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CONST_H } Z_OBJ_P(result)->properties = ht; } else if (Z_TYPE_P(expr) != IS_NULL) { - Z_OBJ_P(result)->properties = ht = zend_new_array(1); + Z_OBJ_P(result)->properties = ht = zend_new_array_assoc(1); expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr); if (IS_CONST == IS_CONST) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr); @@ -17626,7 +17626,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPC } Z_OBJ_P(result)->properties = ht; } else if (Z_TYPE_P(expr) != IS_NULL) { - Z_OBJ_P(result)->properties = ht = zend_new_array(1); + Z_OBJ_P(result)->properties = ht = zend_new_array_assoc(1); expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr); if (IS_TMP_VAR == IS_CONST) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr); @@ -20284,7 +20284,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPC } Z_OBJ_P(result)->properties = ht; } else if (Z_TYPE_P(expr) != IS_NULL) { - Z_OBJ_P(result)->properties = ht = zend_new_array(1); + Z_OBJ_P(result)->properties = ht = zend_new_array_assoc(1); expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr); if (IS_VAR == IS_CONST) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr); @@ -36211,7 +36211,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CAST_SPEC_CV_HANDLER(ZEND_OPCO } Z_OBJ_P(result)->properties = ht; } else if (Z_TYPE_P(expr) != IS_NULL) { - Z_OBJ_P(result)->properties = ht = zend_new_array(1); + Z_OBJ_P(result)->properties = ht = zend_new_array_assoc(1); expr = zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), expr); if (IS_CV == IS_CONST) { if (UNEXPECTED(Z_OPT_REFCOUNTED_P(expr))) Z_ADDREF_P(expr); diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y index 8802d18439ddf..f783bc158a0e1 100644 --- a/ext/json/json_parser.y +++ b/ext/json/json_parser.y @@ -240,7 +240,7 @@ static int php_json_parser_array_append(php_json_parser *parser, zval *array, zv static int php_json_parser_object_create(php_json_parser *parser, zval *object) { if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) { - array_init(object); + array_init_assoc(object); return SUCCESS; } else { return object_init(object); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index ae997b9a60907..5ae1d1d7266f4 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -117,7 +117,8 @@ static void zend_hash_persist(HashTable *ht) void *data = HT_GET_DATA_ADDR(ht); data = zend_shared_memdup_free(data, HT_USED_SIZE(ht)); HT_SET_DATA_ADDR(ht, data); - } else if (ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { + } else if (ht->nNumUsed > HT_MIN_SIZE_UNPACKED && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { + /* TODO any special considerations for this? */ /* compact table */ void *old_data = HT_GET_DATA_ADDR(ht); Bucket *old_buckets = ht->arData; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index a7b347ee0ee2e..d773c7a9ac76e 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -62,7 +62,7 @@ static void zend_hash_persist_calc(HashTable *ht) return; } - if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { + if (!(HT_FLAGS(ht) & HASH_FLAG_PACKED) && ht->nNumUsed > HT_MIN_SIZE_UNPACKED && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { /* compact table */ uint32_t hash_size; diff --git a/ext/standard/array.c b/ext/standard/array.c index 8f326f38d8e5f..86fd729471457 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2633,9 +2633,9 @@ PHP_FUNCTION(compact) or multiple string names, rather than a combination of both. So quickly guess a minimum result size based on that */ if (num_args && Z_TYPE(args[0]) == IS_ARRAY) { - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0]))); + array_init_assoc_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0]))); } else { - array_init_size(return_value, num_args); + array_init_assoc_size(return_value, num_args); } for (i = 0; i < num_args; i++) { @@ -2726,7 +2726,7 @@ PHP_FUNCTION(array_fill_keys) ZEND_PARSE_PARAMETERS_END(); /* Initialize return array */ - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys))); + array_init_assoc_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys))); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) { ZVAL_DEREF(entry); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index a02a26b1a62eb..a7cab5a3c01a9 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1287,7 +1287,7 @@ PHP_FUNCTION(time_nanosleep) if (!nanosleep(&php_req, &php_rem)) { RETURN_TRUE; } else if (errno == EINTR) { - array_init(return_value); + array_init_size(return_value, 2); add_assoc_long_ex(return_value, "seconds", sizeof("seconds")-1, php_rem.tv_sec); add_assoc_long_ex(return_value, "nanoseconds", sizeof("nanoseconds")-1, php_rem.tv_nsec); return; @@ -1528,7 +1528,7 @@ PHP_FUNCTION(error_get_last) ZEND_PARSE_PARAMETERS_NONE(); if (PG(last_error_message)) { - array_init(return_value); + array_init_size(return_value, 4); add_assoc_long_ex(return_value, "type", sizeof("type")-1, PG(last_error_type)); add_assoc_str_ex(return_value, "message", sizeof("message")-1, zend_string_copy(PG(last_error_message))); diff --git a/ext/standard/datetime.c b/ext/standard/datetime.c index ba70db4b06163..264b1a4f01cb4 100644 --- a/ext/standard/datetime.c +++ b/ext/standard/datetime.c @@ -90,7 +90,7 @@ PHP_FUNCTION(strptime) RETURN_FALSE; } - array_init(return_value); + array_init_size(return_value, 9); add_assoc_long(return_value, "tm_sec", parsed_time.tm_sec); add_assoc_long(return_value, "tm_min", parsed_time.tm_min); add_assoc_long(return_value, "tm_hour", parsed_time.tm_hour); diff --git a/ext/standard/dir.c b/ext/standard/dir.c index 547a3dbfd6cbb..ad56b8e224338 100644 --- a/ext/standard/dir.c +++ b/ext/standard/dir.c @@ -572,7 +572,7 @@ PHP_FUNCTION(scandir) RETURN_FALSE; } - array_init(return_value); + array_init_size(return_value, n); for (i = 0; i < n; i++) { add_next_index_str(return_value, namelist[i]); diff --git a/ext/standard/file.c b/ext/standard/file.c index 763409d1813ff..9007ac6282925 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -1531,7 +1531,7 @@ PHP_FUNCTION(fstat) RETURN_FALSE; } - array_init(return_value); + array_init_size(return_value, 26); ZVAL_LONG(&stat_dev, stat_ssb.sb.st_dev); ZVAL_LONG(&stat_ino, stat_ssb.sb.st_ino); diff --git a/ext/standard/scanf.c b/ext/standard/scanf.c index 1464008243fb9..61e7fb06b22c1 100644 --- a/ext/standard/scanf.c +++ b/ext/standard/scanf.c @@ -625,7 +625,7 @@ PHPAPI int php_sscanf_internal( char *string, char *format, zval tmp; /* allocate an array for return */ - array_init(return_value); + array_init_size(return_value, totalVars); for (i = 0; i < totalVars; i++) { ZVAL_NULL(&tmp); diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 7875285f5613f..619255c01bfc6 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -68,7 +68,7 @@ PHP_FUNCTION(stream_socket_pair) RETURN_FALSE; } - array_init(return_value); + array_init_size(return_value, 2); s1 = php_stream_sock_open_from_socket(pair[0], 0); s2 = php_stream_sock_open_from_socket(pair[1], 0); diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index 866977397dd32..90f96224f37f1 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -338,7 +338,7 @@ static void add_token( } } } else if (token_type >= 256) { - array_init(&token); + array_init_size(&token, 3); add_next_index_long(&token, token_type); add_next_index_str(&token, make_str(text, leng, interned_strings)); add_next_index_long(&token, lineno);