com php-src: Make array_splice() to preserve foreac h hash position: Zend/tests/foreach_017.phpt Zend/zend_hash.c Zend/zend_hash.h ext/standard/a rray.c

[email protected] (Rasmus Lerdorf)
Newsgroups php.cvs
Message-ID <[email protected]>
Commit:    08302c0d6d1cab279b9f2129df03a057baddf2ff
Author:    Dmitry Stogov <[email protected]>         Fri, 30 Jan 2015 14:20:46 +0300
Parents:   cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e
Branches:  dstogov-foreach

Link:       http://git.php.net/?p=php-src.git;a=commitdiff;h=08302c0d6d1cab279b9f2129df03a057baddf2ff

Log:
Make array_splice() to preserve foreach hash position

Changed paths:
  A  Zend/tests/foreach_017.phpt
  M  Zend/zend_hash.c
  M  Zend/zend_hash.h
  M  ext/standard/array.c


Diff:
diff --git a/Zend/tests/foreach_017.phpt b/Zend/tests/foreach_017.phpt
new file mode 100644
index 0000000..780d2e9
--- /dev/null
+++ b/Zend/tests/foreach_017.phpt
@@ -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
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c
index 82617ed..8a2f6a2 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;
diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h
index 749846a..d244995 100644
--- a/Zend/zend_hash.h
+++ b/Zend/zend_hash.h
@@ -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()
diff --git a/ext/standard/array.c b/ext/standard/array.c
index 526c921..a7e6089 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -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);
 }
 /* }}} */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.