From 15a23b1218b3e38630d677751a975907daa2cd54 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 Jan 2015 21:05:02 +0300 Subject: Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details) --- Zend/zend_hash.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) (limited to 'Zend/zend_hash.c') diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 480ac64dd1..dd175c7c00 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -193,6 +193,144 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt } } +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_count); + uint32_t idx; + + if (EXPECTED(ht->u.v.nIteratorsCount != 255)) { + ht->u.v.nIteratorsCount++; + } + while (iter != end) { + if (iter->ht == NULL) { + iter->ht = ht; + iter->pos = ht->nInternalPointer; + idx = iter - EG(ht_iterators); + if (idx + 1 > EG(ht_iterators_used)) { + EG(ht_iterators_used) = idx + 1; + } + return idx; + } + iter++; + } + if (EG(ht_iterators) == EG(ht_iterators_slots)) { + EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8)); + memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count)); + } else { + EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8)); + } + iter = EG(ht_iterators) + EG(ht_iterators_count); + EG(ht_iterators_count) += 8; + iter->ht = ht; + iter->pos = ht->nInternalPointer; + memset(iter + 1, 0, sizeof(HashTableIterator) * 7); + idx = iter - EG(ht_iterators); + EG(ht_iterators_used) = idx + 1; + return idx; +} + +ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators) + idx; + + ZEND_ASSERT(idx != (uint32_t)-1); + if (iter->pos == INVALID_IDX) { + return INVALID_IDX; + } else if (UNEXPECTED(iter->ht != ht)) { + if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) { + iter->ht->u.v.nIteratorsCount--; + } + if (EXPECTED(ht->u.v.nIteratorsCount != 255)) { + ht->u.v.nIteratorsCount++; + } + iter->ht = ht; + iter->pos = ht->nInternalPointer; + } + return iter->pos; +} + +ZEND_API void zend_hash_iterator_del(uint32_t idx) +{ + HashTableIterator *iter = EG(ht_iterators) + idx; + + ZEND_ASSERT(idx != (uint32_t)-1); + + if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) { + iter->ht->u.v.nIteratorsCount--; + } + iter->ht = NULL; + + if (idx == EG(ht_iterators_used) - 1) { + while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) { + idx--; + } + EG(ht_iterators_used) = idx; + } +} + +static zend_never_inline void _iterators_del(HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + uint32_t idx; + + while (iter != end) { + if (iter->ht == ht) { + iter->ht = NULL; + } + iter++; + } + + idx = EG(ht_iterators_used); + while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) { + idx--; + } + EG(ht_iterators_used) = idx; +} + +static zend_always_inline void iterators_del(HashTable *ht) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_del(ht); + } +} + +static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == ht->nInternalPointer) { + iter->pos = pos; + } + iter++; + } +} + +static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_update(ht, pos); + } +} + +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == ht->nInternalPointer) { + iter->pos = pos; + } + iter++; + } + } +} + static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_ulong h; @@ -303,6 +441,7 @@ add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, idx); ht->nInternalPointer = idx; } p = ht->arData + idx; @@ -470,6 +609,7 @@ add_to_packed: } ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, h); ht->nInternalPointer = h; } if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { @@ -512,6 +652,7 @@ add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, idx); ht->nInternalPointer = idx; } if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { @@ -600,6 +741,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht) if (i != j) { ht->arData[j] = ht->arData[i]; if (ht->nInternalPointer == i) { + iterators_update(ht, j); ht->nInternalPointer = j; } } @@ -632,9 +774,11 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, while (1) { idx++; if (idx >= ht->nNumUsed) { + iterators_update(ht, INVALID_IDX); ht->nInternalPointer = INVALID_IDX; break; } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { + iterators_update(ht, idx); ht->nInternalPointer = idx; break; } @@ -893,6 +1037,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } while (++p != end); } } + iterators_del(ht); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; } @@ -933,7 +1078,7 @@ ZEND_API void zend_array_destroy(HashTable *ht) } } while (++p != end); } - + iterators_del(ht); SET_INCONSISTENT(HT_DESTROYED); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; -- cgit v1.2.1 From 4c5b385ff53ae9f0b52572e98c4db801f56603b0 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 07:56:37 +0300 Subject: More careful iterators update. --- Zend/zend_hash.c | 65 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 14 deletions(-) (limited to 'Zend/zend_hash.c') diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index dd175c7c00..82617edf18 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -296,35 +296,73 @@ static zend_always_inline void iterators_del(HashTable *ht) } } -static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos) +static zend_never_inline void _iterators_update(HashTable *ht, HashPosition from, HashPosition to) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); while (iter != end) { - if (iter->ht == ht && iter->pos == ht->nInternalPointer) { - iter->pos = pos; + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; } iter++; } } -static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos) +static zend_always_inline void iterators_update(HashTable *ht, HashPosition from, HashPosition to) { if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_update(ht, pos); + _iterators_update(ht, from, to); } } -ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos) +static zend_never_inline void _iterators_update_to_next(HashTable *ht, HashPosition from) +{ + uint32_t to = from; + + while (1) { + to++; + if (to >= ht->nNumUsed) { + to = INVALID_IDX; + break; + } else if (Z_TYPE(ht->arData[to].val) != IS_UNDEF) { + break; + } + } + _iterators_update(ht, from, to); +} + +static zend_always_inline void iterators_update_to_next(HashTable *ht, HashPosition from) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_update_to_next(ht, from); + } +} + +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; + } + iter++; + } + } +} + +ZEND_API void zend_hash_iterators_reset(HashTable *ht) { if (UNEXPECTED(ht->u.v.nIteratorsCount)) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); while (iter != end) { - if (iter->ht == ht && iter->pos == ht->nInternalPointer) { - iter->pos = pos; + if (iter->ht == ht) { + iter->pos = ht->nInternalPointer; } iter++; } @@ -441,9 +479,9 @@ add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { - iterators_update(ht, idx); ht->nInternalPointer = idx; } + iterators_update(ht, INVALID_IDX, idx); p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; @@ -609,9 +647,9 @@ add_to_packed: } ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { - iterators_update(ht, h); ht->nInternalPointer = h; } + iterators_update(ht, INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -652,9 +690,9 @@ add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { - iterators_update(ht, idx); ht->nInternalPointer = idx; } + iterators_update(ht, INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -741,9 +779,9 @@ ZEND_API int zend_hash_rehash(HashTable *ht) if (i != j) { ht->arData[j] = ht->arData[i]; if (ht->nInternalPointer == i) { - iterators_update(ht, j); ht->nInternalPointer = j; } + iterators_update(ht, i, j); } nIndex = ht->arData[j].h & ht->nTableMask; Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; @@ -770,15 +808,14 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)); } ht->nNumOfElements--; + iterators_update_to_next(ht, idx); if (ht->nInternalPointer == idx) { while (1) { idx++; if (idx >= ht->nNumUsed) { - iterators_update(ht, INVALID_IDX); ht->nInternalPointer = INVALID_IDX; break; } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - iterators_update(ht, idx); ht->nInternalPointer = idx; break; } -- cgit v1.2.1 From 08302c0d6d1cab279b9f2129df03a057baddf2ff Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 14:20:46 +0300 Subject: Make array_splice() to preserve foreach hash position --- Zend/zend_hash.c | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'Zend/zend_hash.c') diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 82617edf18..8a2f6a26c3 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -354,21 +354,6 @@ ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashP } } -ZEND_API void zend_hash_iterators_reset(HashTable *ht) -{ - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - HashTableIterator *iter = EG(ht_iterators); - HashTableIterator *end = iter + EG(ht_iterators_used); - - while (iter != end) { - if (iter->ht == ht) { - iter->pos = ht->nInternalPointer; - } - iter++; - } - } -} - static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_ulong h; -- cgit v1.2.1 From fb2d079645829b12ed4e55a461034df6400bc430 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Fri, 30 Jan 2015 18:08:05 +0300 Subject: API cleanup --- Zend/zend_hash.c | 137 +++++++++++++++++++++++++++---------------------------- 1 file changed, 68 insertions(+), 69 deletions(-) (limited to 'Zend/zend_hash.c') diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 8a2f6a26c3..815444bbf5 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -269,7 +269,7 @@ ZEND_API void zend_hash_iterator_del(uint32_t idx) } } -static zend_never_inline void _iterators_del(HashTable *ht) +static zend_never_inline void _zend_hash_iterators_remove(HashTable *ht) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); @@ -289,68 +289,41 @@ static zend_never_inline void _iterators_del(HashTable *ht) EG(ht_iterators_used) = idx; } -static zend_always_inline void iterators_del(HashTable *ht) +static zend_always_inline void zend_hash_iterators_remove(HashTable *ht) { if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_del(ht); + _zend_hash_iterators_remove(ht); } } -static zend_never_inline void _iterators_update(HashTable *ht, HashPosition from, HashPosition to) +ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_used); + HashPosition res = INVALID_IDX; + uint32_t idx; while (iter != end) { - if (iter->ht == ht && iter->pos == from) { - iter->pos = to; + if (iter->ht == ht) { + if (iter->pos >= start && iter->pos < res) { + res = iter->pos; + } } iter++; } + return res; } -static zend_always_inline void iterators_update(HashTable *ht, HashPosition from, HashPosition to) -{ - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_update(ht, from, to); - } -} - -static zend_never_inline void _iterators_update_to_next(HashTable *ht, HashPosition from) -{ - uint32_t to = from; - - while (1) { - to++; - if (to >= ht->nNumUsed) { - to = INVALID_IDX; - break; - } else if (Z_TYPE(ht->arData[to].val) != IS_UNDEF) { - break; - } - } - _iterators_update(ht, from, to); -} - -static zend_always_inline void iterators_update_to_next(HashTable *ht, HashPosition from) -{ - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - _iterators_update_to_next(ht, from); - } -} - -ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) { - if (UNEXPECTED(ht->u.v.nIteratorsCount)) { - HashTableIterator *iter = EG(ht_iterators); - HashTableIterator *end = iter + EG(ht_iterators_used); + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); - while (iter != end) { - if (iter->ht == ht && iter->pos == from) { - iter->pos = to; - } - iter++; + while (iter != end) { + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; } + iter++; } } @@ -466,7 +439,7 @@ add_to_hash: if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } - iterators_update(ht, INVALID_IDX, idx); + zend_hash_iterators_update(ht, INVALID_IDX, idx); p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; @@ -634,7 +607,7 @@ add_to_packed: if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = h; } - iterators_update(ht, INVALID_IDX, h); + zend_hash_iterators_update(ht, INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -677,7 +650,7 @@ add_to_hash: if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } - iterators_update(ht, INVALID_IDX, idx); + zend_hash_iterators_update(ht, INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -758,20 +731,42 @@ ZEND_API int zend_hash_rehash(HashTable *ht) } memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t)); - for (i = 0, j = 0; i < ht->nNumUsed; i++) { - p = ht->arData + i; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (i != j) { - ht->arData[j] = ht->arData[i]; - if (ht->nInternalPointer == i) { - ht->nInternalPointer = j; + if (EXPECTED(ht->u.v.nIteratorsCount == 0)) { + for (i = 0, j = 0; i < ht->nNumUsed; i++) { + p = ht->arData + i; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (i != j) { + ht->arData[j] = ht->arData[i]; + if (ht->nInternalPointer == i) { + ht->nInternalPointer = j; + } } - iterators_update(ht, i, j); + nIndex = ht->arData[j].h & ht->nTableMask; + Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; + ht->arHash[nIndex] = j; + j++; + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, 0); + + for (i = 0, j = 0; i < ht->nNumUsed; i++) { + p = ht->arData + i; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (i != j) { + ht->arData[j] = ht->arData[i]; + if (ht->nInternalPointer == i) { + ht->nInternalPointer = j; + } + if (i == iter_pos) { + zend_hash_iterators_update(ht, i, j); + iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1); + } + } + nIndex = ht->arData[j].h & ht->nTableMask; + Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; + ht->arHash[nIndex] = j; + j++; } - nIndex = ht->arData[j].h & ht->nTableMask; - Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; - ht->arHash[nIndex] = j; - j++; } ht->nNumUsed = j; return SUCCESS; @@ -793,18 +788,22 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)); } ht->nNumOfElements--; - iterators_update_to_next(ht, idx); - if (ht->nInternalPointer == idx) { + if (ht->nInternalPointer == idx || UNEXPECTED(ht->u.v.nIteratorsCount)) { + uint32_t new_idx = idx; + while (1) { - idx++; - if (idx >= ht->nNumUsed) { - ht->nInternalPointer = INVALID_IDX; + new_idx++; + if (new_idx >= ht->nNumUsed) { + new_idx = INVALID_IDX; break; - } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - ht->nInternalPointer = idx; + } else if (Z_TYPE(ht->arData[new_idx].val) != IS_UNDEF) { break; } } + if (ht->nInternalPointer == idx) { + ht->nInternalPointer = new_idx; + } + zend_hash_iterators_update(ht, idx, new_idx); } if (p->key) { zend_string_release(p->key); @@ -1059,7 +1058,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } while (++p != end); } } - iterators_del(ht); + zend_hash_iterators_remove(ht); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; } @@ -1100,7 +1099,7 @@ ZEND_API void zend_array_destroy(HashTable *ht) } } while (++p != end); } - iterators_del(ht); + zend_hash_iterators_remove(ht); SET_INCONSISTENT(HT_DESTROYED); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; -- cgit v1.2.1 From 1e41295097576dbce6c197ddb7507c07ccae3cbe Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Sat, 31 Jan 2015 07:28:58 +0300 Subject: Generalize HashTableIterator API to allows its usage without iinvolvement of HashTable.nInternalPonter --- Zend/zend_hash.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Zend/zend_hash.c') diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 815444bbf5..195b5e48f0 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -193,7 +193,7 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt } } -ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht, HashPosition pos) { HashTableIterator *iter = EG(ht_iterators); HashTableIterator *end = iter + EG(ht_iterators_count); @@ -205,7 +205,7 @@ ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) while (iter != end) { if (iter->ht == NULL) { iter->ht = ht; - iter->pos = ht->nInternalPointer; + iter->pos = pos; idx = iter - EG(ht_iterators); if (idx + 1 > EG(ht_iterators_used)) { EG(ht_iterators_used) = idx + 1; @@ -223,7 +223,7 @@ ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) iter = EG(ht_iterators) + EG(ht_iterators_count); EG(ht_iterators_count) += 8; iter->ht = ht; - iter->pos = ht->nInternalPointer; + iter->pos = pos; memset(iter + 1, 0, sizeof(HashTableIterator) * 7); idx = iter - EG(ht_iterators); EG(ht_iterators_used) = idx + 1; -- cgit v1.2.1