1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Make array_splice() to preserve foreach hash position

This commit is contained in:
Dmitry Stogov
2015-01-30 14:20:46 +03:00
parent cc4b7be41e
commit 08302c0d6d
4 changed files with 138 additions and 22 deletions

111
Zend/tests/foreach_017.phpt Normal file
View File

@@ -0,0 +1,111 @@
--TEST--
array_unshift() function precerve foreach by reference iterator pointer
--FILE--
<?php
/* remove before */
$done = 0;
$a = [0,1,2,3,4];
foreach($a as &$v) {
echo "$v\n";
if (!$done && $v == 3) {
$done = 1;
array_splice($a, 1, 2);
}
}
echo "\n";
/* remove after */
$done = 0;
$a = [0,1,2,3,4];
foreach($a as &$v) {
echo "$v\n";
if (!$done && $v == 0) {
$done = 1;
array_splice($a, 2, 2);
}
}
echo "\n";
/* remove current */
$done = 0;
$a = [0,1,2,3,4];
foreach($a as &$v) {
echo "$v\n";
if (!$done && $v == 2) {
$done = 1;
array_splice($a, 1, 3);
}
}
echo "\n";
$replacement = ['x', 'y', 'z'];
/* replace before */
$done = 0;
$a = [0,1,2,3,4];
foreach($a as &$v) {
echo "$v\n";
if ($done && $v == 3) {
$done = 1;
array_splice($a, 1, 2, $replacement);
}
}
echo "\n";
/* replace after */
$done = 0;
$a = [0,1,2,3,4];
foreach($a as &$v) {
echo "$v\n";
if (!$done && $v == 0) {
$done = 1;
array_splice($a, 2, 2, $replacement);
}
}
echo "\n";
/* replace current */
$done = 0;
$a = [0,1,2,3,4];
foreach($a as &$v) {
echo "$v\n";
if (!$done && $v == 2) {
$done = 1;
array_splice($a, 1, 3, $replacement);
}
}
echo "\n";
?>
--EXPECT--
0
1
2
3
4
0
1
4
0
1
2
4
0
1
2
3
4
0
1
x
y
z
4
0
1
2
4

View File

@@ -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;

View File

@@ -230,7 +230,6 @@ ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht);
ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht);
ZEND_API void zend_hash_iterator_del(uint32_t idx);
ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to);
ZEND_API void zend_hash_iterators_reset(HashTable *ht);
END_EXTERN_C()

View File

@@ -1989,7 +1989,6 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
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;
pos++;
/* Get entry and increase reference count */
entry = &p->val;
@@ -1999,6 +1998,10 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
} else {
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
pos++;
}
/* If hash for removed entries exists, go until offset+length and copy the entries to it */
@@ -2024,10 +2027,12 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
}
}
} else { /* otherwise just skip those entries */
for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
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;
pos++;
pos2++;
if (p->key == NULL) {
zend_hash_index_del(in_hash, p->h);
} else {
@@ -2045,6 +2050,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
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();
}
@@ -2058,13 +2064,28 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re
} else {
zend_hash_add_new(&out_hash, p->key, entry);
}
if (idx != pos) {
zend_hash_iterators_update(in_hash, idx, pos);
}
pos++;
}
zend_hash_internal_pointer_reset(&out_hash);
/* replace HashTable data */
in_hash->u.v.nIteratorsCount = 0;
in_hash->pDestructor = NULL;
zend_hash_destroy(in_hash);
*in_hash = out_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->arHash = out_hash.arHash;
in_hash->pDestructor = out_hash.pDestructor;
zend_hash_internal_pointer_reset(in_hash);
}
/* }}} */