From: Dmitry Stogov Date: Wed, 21 Mar 2018 23:41:49 +0000 (+0300) Subject: Improve HashTable iterators handling: X-Git-Tag: php-7.3.0alpha1~158 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d7f2dc4ec651628e10213625db6aee3559e214a9;p=php Improve HashTable iterators handling: - Avoid iterators check/update on each HashTable update opration - Keep position equal (or above) nNumUsed instead of HT_INVALID_IDX - Fixed iterators handling in array_unshift() --- diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index b209cbb70e..d5de72e95a 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -166,7 +166,7 @@ ZEND_API const HashTable zend_empty_array = { .nNumUsed = 0, .nNumOfElements = 0, .nTableSize = HT_MIN_SIZE, - .nInternalPointer = HT_INVALID_IDX, + .nInternalPointer = 0, .nNextFreeElement = 0, .pDestructor = ZVAL_PTR_DTOR }; @@ -180,7 +180,7 @@ static zend_always_inline void _zend_hash_init_int(HashTable *ht, uint32_t nSize HT_SET_DATA_ADDR(ht, &uninitialized_bucket); ht->nNumUsed = 0; ht->nNumOfElements = 0; - ht->nInternalPointer = HT_INVALID_IDX; + ht->nInternalPointer = 0; ht->nNextFreeElement = 0; ht->pDestructor = pDestructor; ht->nTableSize = zend_hash_check_size(nSize); @@ -313,6 +313,31 @@ ZEND_API uint32_t zend_array_count(HashTable *ht) } /* }}} */ +static zend_always_inline HashPosition _zend_hash_get_first_pos(const HashTable *ht) +{ + HashPosition pos = 0; + + while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arData[pos].val)) { + pos++; + } + return pos; +} + +static zend_always_inline HashPosition _zend_hash_get_current_pos(const HashTable *ht) +{ + HashPosition pos = ht->nInternalPointer; + + if (pos == 0) { + pos = _zend_hash_get_first_pos(ht); + } + return pos; +} + +ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht) +{ + return _zend_hash_get_current_pos(ht); +} + ZEND_API uint32_t ZEND_FASTCALL zend_hash_iterator_add(HashTable *ht, HashPosition pos) { HashTableIterator *iter = EG(ht_iterators); @@ -355,9 +380,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTab HashTableIterator *iter = EG(ht_iterators) + idx; ZEND_ASSERT(idx != (uint32_t)-1); - if (iter->pos == HT_INVALID_IDX) { - return HT_INVALID_IDX; - } else if (UNEXPECTED(iter->ht != ht)) { + if (UNEXPECTED(iter->ht != ht)) { if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR) && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) { HT_DEC_ITERATORS_COUNT(iter->ht); @@ -366,7 +389,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTab HT_INC_ITERATORS_COUNT(ht); } iter->ht = ht; - iter->pos = ht->nInternalPointer; + iter->pos = _zend_hash_get_current_pos(ht); } return iter->pos; } @@ -377,9 +400,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval HashTableIterator *iter = EG(ht_iterators) + idx; ZEND_ASSERT(idx != (uint32_t)-1); - if (iter->pos == HT_INVALID_IDX) { - return HT_INVALID_IDX; - } else if (UNEXPECTED(iter->ht != ht)) { + if (UNEXPECTED(iter->ht != ht)) { if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR) && EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) { HT_DEC_ITERATORS_COUNT(iter->ht); @@ -390,7 +411,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval HT_INC_ITERATORS_COUNT(ht); } iter->ht = ht; - iter->pos = ht->nInternalPointer; + iter->pos = _zend_hash_get_current_pos(ht); } return iter->pos; } @@ -439,7 +460,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(HashTable *ht, { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); - HashPosition res = HT_INVALID_IDX; + HashPosition res = ht->nNumUsed; while (iter != end) { if (iter->ht == ht) { @@ -465,6 +486,19 @@ ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(HashTable *ht, HashPosit } } +ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(HashTable *ht, HashPosition step) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht) { + iter->pos += step; + } + iter++; + } +} + static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key, zend_bool known_hash) { zend_ulong h; @@ -610,10 +644,6 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; - if (ht->nInternalPointer == HT_INVALID_IDX) { - ht->nInternalPointer = idx; - } - zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); arData = ht->arData; p = arData + idx; p->key = key; @@ -681,10 +711,6 @@ static zend_always_inline zval *_zend_hash_str_add_or_update_i(HashTable *ht, co add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; - if (ht->nInternalPointer == HT_INVALID_IDX) { - ht->nInternalPointer = idx; - } - zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); p = ht->arData + idx; p->key = key = zend_string_init(str, len, GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); p->h = ZSTR_H(key) = h; @@ -837,10 +863,6 @@ add_to_packed: ht->nNumUsed = h + 1; } ht->nNumOfElements++; - if (ht->nInternalPointer == HT_INVALID_IDX) { - ht->nInternalPointer = h; - } - zend_hash_iterators_update(ht, HT_INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -875,10 +897,6 @@ convert_to_hash: add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; - if (ht->nInternalPointer == HT_INVALID_IDX) { - ht->nInternalPointer = idx; - } - zend_hash_iterators_update(ht, HT_INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -1055,7 +1073,6 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, while (1) { new_idx++; if (new_idx >= ht->nNumUsed) { - new_idx = HT_INVALID_IDX; break; } else if (Z_TYPE(ht->arData[new_idx].val) != IS_UNDEF) { break; @@ -1474,7 +1491,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_clean(HashTable *ht) ht->nNumUsed = 0; ht->nNumOfElements = 0; ht->nNextFreeElement = 0; - ht->nInternalPointer = HT_INVALID_IDX; + ht->nInternalPointer = 0; } ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht) @@ -1513,7 +1530,7 @@ ZEND_API void ZEND_FASTCALL zend_symtable_clean(HashTable *ht) ht->nNumUsed = 0; ht->nNumOfElements = 0; ht->nNextFreeElement = 0; - ht->nInternalPointer = HT_INVALID_IDX; + ht->nInternalPointer = 0; } ZEND_API void ZEND_FASTCALL zend_hash_graceful_destroy(HashTable *ht) @@ -1681,20 +1698,15 @@ ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, uint32_t idx; Bucket *p; zval *new_entry, *data; - zend_bool setTargetPointer; IS_CONSISTENT(source); IS_CONSISTENT(target); HT_ASSERT_RC1(target); - setTargetPointer = (target->nInternalPointer == HT_INVALID_IDX); for (idx = 0; idx < source->nNumUsed; idx++) { p = source->arData + idx; if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; - if (setTargetPointer && source->nInternalPointer == idx) { - target->nInternalPointer = HT_INVALID_IDX; - } /* INDIRECT element may point to UNDEF-ined slots */ data = &p->val; if (Z_TYPE_P(data) == IS_INDIRECT) { @@ -1712,13 +1724,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_copy(HashTable *target, HashTable *source, pCopyConstructor(new_entry); } } - if (target->nInternalPointer == HT_INVALID_IDX && target->nNumOfElements > 0) { - idx = 0; - while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) { - idx++; - } - target->nInternalPointer = idx; - } } @@ -1840,7 +1845,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nNumUsed = 0; target->nNumOfElements = 0; target->nNextFreeElement = 0; - target->nInternalPointer = HT_INVALID_IDX; + target->nInternalPointer = 0; HT_SET_DATA_ADDR(target, &uninitialized_bucket); } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) { HT_FLAGS(target) = HT_FLAGS(source); @@ -1851,13 +1856,6 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target))); target->nInternalPointer = source->nInternalPointer; memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source)); - if (target->nInternalPointer == HT_INVALID_IDX) { - idx = 0; - while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) { - idx++; - } - target->nInternalPointer = idx; - } } else if (HT_FLAGS(source) & HASH_FLAG_PACKED) { HT_FLAGS(target) = HT_FLAGS(source); target->nTableMask = HT_MIN_MASK; @@ -1865,7 +1863,10 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nNumOfElements = source->nNumOfElements; target->nNextFreeElement = source->nNextFreeElement; HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target))); - target->nInternalPointer = source->nInternalPointer; + target->nInternalPointer = + (source->nInternalPointer < source->nNumUsed) ? + source->nInternalPointer : 0; + HT_HASH_RESET_PACKED(target); if (HT_IS_WITHOUT_HOLES(target)) { @@ -1873,18 +1874,13 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) } else { zend_array_dup_packed_elements(source, target, 1); } - if (target->nInternalPointer == HT_INVALID_IDX) { - idx = 0; - while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) { - idx++; - } - target->nInternalPointer = idx; - } } else { HT_FLAGS(target) = HT_FLAGS(source); target->nTableMask = source->nTableMask; target->nNextFreeElement = source->nNextFreeElement; - target->nInternalPointer = source->nInternalPointer; + target->nInternalPointer = + (source->nInternalPointer < source->nNumUsed) ? + source->nInternalPointer : 0; HT_SET_DATA_ADDR(target, emalloc(HT_SIZE(target))); HT_HASH_RESET(target); @@ -1904,9 +1900,6 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) } target->nNumUsed = idx; target->nNumOfElements = idx; - if (target->nInternalPointer == HT_INVALID_IDX) { - target->nInternalPointer = 0; - } } return target; } @@ -1963,13 +1956,6 @@ ZEND_API void ZEND_FASTCALL _zend_hash_merge(HashTable *target, HashTable *sourc } } } - if (target->nNumOfElements > 0) { - idx = 0; - while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) { - idx++; - } - target->nInternalPointer = idx; - } } @@ -2003,13 +1989,6 @@ ZEND_API void ZEND_FASTCALL zend_hash_merge_ex(HashTable *target, HashTable *sou } } } - if (target->nNumOfElements > 0) { - idx = 0; - while (Z_TYPE(target->arData[idx].val) == IS_UNDEF) { - idx++; - } - target->nInternalPointer = idx; - } } @@ -2120,18 +2099,9 @@ ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zen ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos) { - uint32_t idx; - IS_CONSISTENT(ht); HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1); - - for (idx = 0; idx < ht->nNumUsed; idx++) { - if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - *pos = idx; - return; - } - } - *pos = HT_INVALID_IDX; + *pos = _zend_hash_get_first_pos(ht); } @@ -2153,7 +2123,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Has return; } } - *pos = HT_INVALID_IDX; + *pos = ht->nNumUsed; } @@ -2164,11 +2134,18 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition IS_CONSISTENT(ht); HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1); - if (idx != HT_INVALID_IDX) { + if (idx < ht->nNumUsed) { + if (idx == 0) { + idx = _zend_hash_get_first_pos(ht); + if (idx >= ht->nNumUsed) { + *pos = idx; + return SUCCESS; + } + } while (1) { idx++; if (idx >= ht->nNumUsed) { - *pos = HT_INVALID_IDX; + *pos = ht->nNumUsed; return SUCCESS; } if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { @@ -2177,7 +2154,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition } } } else { - return FAILURE; + return FAILURE; } } @@ -2188,7 +2165,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPositi IS_CONSISTENT(ht); HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1); - if (idx != HT_INVALID_IDX) { + if (idx < ht->nNumUsed) { while (idx > 0) { idx--; if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { @@ -2196,7 +2173,7 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPositi return SUCCESS; } } - *pos = HT_INVALID_IDX; + *pos = ht->nNumUsed; return SUCCESS; } else { return FAILURE; @@ -2211,7 +2188,13 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zen Bucket *p; IS_CONSISTENT(ht); - if (idx != HT_INVALID_IDX) { + if (idx < ht->nNumUsed) { + if (idx == 0) { + idx = _zend_hash_get_first_pos(ht); + if (idx >= ht->nNumUsed) { + return HASH_KEY_NON_EXISTENT; + } + } p = ht->arData + idx; if (p->key) { *str_index = p->key; @@ -2230,9 +2213,16 @@ ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *h Bucket *p; IS_CONSISTENT(ht); - if (idx == HT_INVALID_IDX) { + if (idx >= ht->nNumUsed) { ZVAL_NULL(key); } else { + if (idx == 0) { + idx = _zend_hash_get_first_pos(ht); + if (idx >= ht->nNumUsed) { + ZVAL_NULL(key); + return; + } + } p = ht->arData + idx; if (p->key) { ZVAL_STR_COPY(key, p->key); @@ -2248,7 +2238,13 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, Hash Bucket *p; IS_CONSISTENT(ht); - if (idx != HT_INVALID_IDX) { + if (idx < ht->nNumUsed) { + if (idx == 0) { + idx = _zend_hash_get_first_pos(ht); + if (idx >= ht->nNumUsed) { + return HASH_KEY_NON_EXISTENT; + } + } p = ht->arData + idx; if (p->key) { return HASH_KEY_IS_STRING; @@ -2266,7 +2262,13 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(HashTable *ht, HashPo Bucket *p; IS_CONSISTENT(ht); - if (idx != HT_INVALID_IDX) { + if (idx < ht->nNumUsed) { + if (idx == 0) { + idx = _zend_hash_get_first_pos(ht); + if (idx >= ht->nNumUsed) { + return NULL; + } + } p = ht->arData + idx; return &p->val; } else { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 1cdf6a82d3..5b14f51f2d 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -230,6 +230,8 @@ ZEND_API zend_bool ZEND_FASTCALL zend_hash_str_exists(const HashTable *ht, const ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong h); /* traversing */ +ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht); + #define zend_hash_has_more_elements_ex(ht, pos) \ (zend_hash_get_current_key_type_ex(ht, pos) == HASH_KEY_NON_EXISTENT ? FAILURE : SUCCESS) ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos); @@ -304,6 +306,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx); ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start); ZEND_API void ZEND_FASTCALL _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to); +ZEND_API void ZEND_FASTCALL zend_hash_iterators_advance(HashTable *ht, HashPosition step); static zend_always_inline void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) { @@ -1091,7 +1094,7 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, __fill_ht->nNumUsed = __fill_idx; \ __fill_ht->nNumOfElements = __fill_idx; \ __fill_ht->nNextFreeElement = __fill_idx; \ - __fill_ht->nInternalPointer = __fill_idx ? 0 : HT_INVALID_IDX; \ + __fill_ht->nInternalPointer = 0; \ } while (0) static zend_always_inline zval *_zend_hash_append(HashTable *ht, zend_string *key, zval *zv) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 0979206326..93cfc88fbf 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -69,7 +69,6 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ zobj->properties = zend_new_array(ce->default_properties_count); if (ce->default_properties_count) { zend_hash_real_init(zobj->properties, 0); - zobj->properties->nInternalPointer = 0; ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop_info) { if (/*prop_info->ce == ce &&*/ (prop_info->flags & ZEND_ACC_STATIC) == 0) { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f967ba8595..a411bc42b9 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5963,10 +5963,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY, JMP_ADDR) ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } - if (++pos >= fe_ht->nNumUsed) { - pos = HT_INVALID_IDX; - } - EG(ht_iterators)[Z_FE_ITER_P(array)].pos = pos; + EG(ht_iterators)[Z_FE_ITER_P(array)].pos = pos + 1; } else { if (EXPECTED(++iter->index > 0)) { /* This could cause an endless loop if index becomes zero again. @@ -6079,10 +6076,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); } } - if (++pos >= fe_ht->nNumUsed) { - pos = HT_INVALID_IDX; - } - EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos + 1; } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { zend_object_iterator *iter; @@ -6128,10 +6122,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY, JMP_ADDR) ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } - if (++pos >= fe_ht->nNumUsed) { - pos = HT_INVALID_IDX; - } - EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos + 1; } else { if (++iter->index > 0) { /* This could cause an endless loop if index becomes zero again. diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d7c94e514e..34fdcbd6bb 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -21657,10 +21657,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZE ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } - if (++pos >= fe_ht->nNumUsed) { - pos = HT_INVALID_IDX; - } - EG(ht_iterators)[Z_FE_ITER_P(array)].pos = pos; + EG(ht_iterators)[Z_FE_ITER_P(array)].pos = pos + 1; } else { if (EXPECTED(++iter->index > 0)) { /* This could cause an endless loop if index becomes zero again. @@ -21773,10 +21770,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z ZVAL_STR_COPY(EX_VAR(opline->result.var), p->key); } } - if (++pos >= fe_ht->nNumUsed) { - pos = HT_INVALID_IDX; - } - EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos + 1; } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { zend_object_iterator *iter; @@ -21822,10 +21816,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z ZVAL_STRINGL(EX_VAR(opline->result.var), prop_name, prop_name_len); } } - if (++pos >= fe_ht->nNumUsed) { - pos = HT_INVALID_IDX; - } - EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = pos + 1; } else { if (++iter->index > 0) { /* This could cause an endless loop if index becomes zero again. diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index f63609417e..fe98d77d3b 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -181,7 +181,7 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source) ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = NULL; HT_FLAGS(ht) = (HT_FLAGS(source) & (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS)); - ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX; + ht->nInternalPointer = 0; if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { ht->arData = source->arData; @@ -232,7 +232,7 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = ZEND_FUNCTION_DTOR; HT_FLAGS(ht) = (HT_FLAGS(source) & (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS)); - ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX; + ht->nInternalPointer = 0; if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { ht->arData = source->arData; @@ -290,7 +290,7 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla ht->nNextFreeElement = source->nNextFreeElement; ht->pDestructor = NULL; HT_FLAGS(ht) = (HT_FLAGS(source) & (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS)); - ht->nInternalPointer = source->nNumOfElements ? 0 : HT_INVALID_IDX; + ht->nInternalPointer = 0; if (!(HT_FLAGS(ht) & HASH_FLAG_INITIALIZED)) { ht->arData = source->arData; @@ -492,7 +492,7 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source) _zend_hash_append_ptr(target, p->key, Z_PTR(p->val)); } } - target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; + target->nInternalPointer = 0; return; failure: @@ -536,7 +536,7 @@ static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable _zend_hash_append_ptr(target, p->key, ARENA_REALLOC(Z_PTR(p->val))); } } - target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; + target->nInternalPointer = 0; return; failure: @@ -592,7 +592,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, uni } } } - target->nInternalPointer = target->nNumOfElements ? 0 : HT_INVALID_IDX; + target->nInternalPointer = 0; return; } diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index d0a1f68215..3135f82f81 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -134,7 +134,7 @@ static int spl_array_skip_protected(spl_array_object *intern, HashTable *aht); static zend_never_inline void spl_array_create_ht_iter(HashTable *ht, spl_array_object* intern) /* {{{ */ { - intern->ht_iter = zend_hash_iterator_add(ht, ht->nInternalPointer); + intern->ht_iter = zend_hash_iterator_add(ht, zend_hash_get_current_pos(ht)); zend_hash_internal_pointer_reset_ex(ht, &EG(ht_iterators)[intern->ht_iter].pos); spl_array_skip_protected(intern, ht); } @@ -1419,7 +1419,7 @@ static int spl_array_object_count_elements_helper(spl_array_object *intern, zend pos = *pos_ptr; *count = 0; spl_array_rewind(intern); - while (*pos_ptr != HT_INVALID_IDX && spl_array_next(intern) == SUCCESS) { + while (*pos_ptr < aht->nNumUsed && spl_array_next(intern) == SUCCESS) { (*count)++; } *pos_ptr = pos; diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 295f7765d2..1c3911a25e 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -234,7 +234,7 @@ static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zval intern = emalloc(sizeof(spl_SplObjectStorage) + zend_object_properties_size(parent)); memset(intern, 0, sizeof(spl_SplObjectStorage) - sizeof(zval)); - intern->pos = HT_INVALID_IDX; + intern->pos = 0; zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); diff --git a/ext/standard/array.c b/ext/standard/array.c index 68061b83ce..062070d947 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2684,7 +2684,6 @@ PHP_FUNCTION(array_fill) zend_hash_real_init(Z_ARRVAL_P(return_value), 1); Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num); Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num; - Z_ARRVAL_P(return_value)->nInternalPointer = (uint32_t)start_key; Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num); if (Z_REFCOUNTED_P(val)) { @@ -3428,36 +3427,22 @@ PHP_FUNCTION(array_unshift) Z_TRY_ADDREF(args[i]); zend_hash_next_index_insert_new(&new_hash, &args[i]); } - if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) { - 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(); + 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(); + + if (EXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) { + zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc); + HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack))); + HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0); } /* replace HashTable data */ - HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0); Z_ARRVAL_P(stack)->pDestructor = NULL; zend_hash_destroy(Z_ARRVAL_P(stack)); diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt index ba1bc89628..cd1e0af96e 100644 --- a/tests/lang/foreachLoop.013.phpt +++ b/tests/lang/foreachLoop.013.phpt @@ -488,18 +488,21 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=3; $v=v.2 + iteration 1: $k=2; $v=v.1 + iteration 2: $k=4; $v=v.2 --> State of array after loop: -array(5) { +array(6) { [0]=> - string(5) "new.1" + string(5) "new.2" [1]=> - string(5) "new.0" + string(5) "new.1" [2]=> - string(3) "v.0" + string(5) "new.0" [3]=> - string(3) "v.1" + string(3) "v.0" [4]=> + string(3) "v.1" + [5]=> &string(3) "v.2" } @@ -517,19 +520,25 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=4; $v=v.3 + iteration 1: $k=2; $v=v.1 + iteration 2: $k=4; $v=v.2 + iteration 3: $k=6; $v=v.3 --> State of array after loop: -array(6) { +array(8) { [0]=> - string(5) "new.1" + string(5) "new.3" [1]=> - string(5) "new.0" + string(5) "new.2" [2]=> - string(3) "v.0" + string(5) "new.1" [3]=> - string(3) "v.1" + string(5) "new.0" [4]=> - string(3) "v.2" + string(3) "v.0" [5]=> + string(3) "v.1" + [6]=> + string(3) "v.2" + [7]=> &string(3) "v.3" } diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt index f7f5389395..13333ccf4d 100644 --- a/tests/lang/foreachLoop.015.phpt +++ b/tests/lang/foreachLoop.015.phpt @@ -490,18 +490,21 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=3; $v=v.2 + iteration 1: $k=2; $v=v.1 + iteration 2: $k=4; $v=v.2 --> State of array after loop: -array(5) { +array(6) { [0]=> - string(5) "new.1" + string(5) "new.2" [1]=> - string(5) "new.0" + string(5) "new.1" [2]=> - string(3) "v.0" + string(5) "new.0" [3]=> - string(3) "v.1" + string(3) "v.0" [4]=> + string(3) "v.1" + [5]=> &string(3) "v.2" } @@ -519,19 +522,25 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=4; $v=v.3 + iteration 1: $k=2; $v=v.1 + iteration 2: $k=4; $v=v.2 + iteration 3: $k=6; $v=v.3 --> State of array after loop: -array(6) { +array(8) { [0]=> - string(5) "new.1" + string(5) "new.3" [1]=> - string(5) "new.0" + string(5) "new.2" [2]=> - string(3) "v.0" + string(5) "new.1" [3]=> - string(3) "v.1" + string(5) "new.0" [4]=> - string(3) "v.2" + string(3) "v.0" [5]=> + string(3) "v.1" + [6]=> + string(3) "v.2" + [7]=> &string(3) "v.3" }