From: Dmitry Stogov Date: Fri, 30 Jan 2015 09:24:31 +0000 (+0300) Subject: Make internal function, operation on array passed by reference, to preserve foreach... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e;p=php Make internal function, operation on array passed by reference, to preserve foreach hash position --- diff --git a/Zend/tests/foreach_011.phpt b/Zend/tests/foreach_011.phpt new file mode 100644 index 0000000000..e91426fb27 --- /dev/null +++ b/Zend/tests/foreach_011.phpt @@ -0,0 +1,19 @@ +--TEST-- +sort() functions precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +2 +1 +0 diff --git a/Zend/tests/foreach_012.phpt b/Zend/tests/foreach_012.phpt new file mode 100644 index 0000000000..5e5538cd4d --- /dev/null +++ b/Zend/tests/foreach_012.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_walk() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +14 +15 \ No newline at end of file diff --git a/Zend/tests/foreach_013.phpt b/Zend/tests/foreach_013.phpt new file mode 100644 index 0000000000..cfbb3d7f79 --- /dev/null +++ b/Zend/tests/foreach_013.phpt @@ -0,0 +1,17 @@ +--TEST-- +array_push() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +4 diff --git a/Zend/tests/foreach_014.phpt b/Zend/tests/foreach_014.phpt new file mode 100644 index 0000000000..8d0ac582a9 --- /dev/null +++ b/Zend/tests/foreach_014.phpt @@ -0,0 +1,15 @@ +--TEST-- +array_pop() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 diff --git a/Zend/tests/foreach_015.phpt b/Zend/tests/foreach_015.phpt new file mode 100644 index 0000000000..adc8085f34 --- /dev/null +++ b/Zend/tests/foreach_015.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_shift() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +4 +array(0) { +} \ No newline at end of file diff --git a/Zend/tests/foreach_016.phpt b/Zend/tests/foreach_016.phpt new file mode 100644 index 0000000000..423c8dd0a6 --- /dev/null +++ b/Zend/tests/foreach_016.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_unshift() function precerve foreach by reference iterator pointer +--FILE-- + +--EXPECT-- +1 +2 +3 +int(11) diff --git a/ext/standard/array.c b/ext/standard/array.c index 49c6cd0789..526c9210c1 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1877,26 +1877,49 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ hash = Z_ARRVAL_P(array); n_left = n_elems; - if (hash->nNumUsed != hash->nNumOfElements) { - for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { - p = hash->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (j != idx) { - hash->arData[j] = *p; + if (EXPECTED(hash->u.v.nIteratorsCount == 0)) { + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + } + j++; } - j++; } - } - while (--n_left) { - rnd_idx = php_rand(); - RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); - if (rnd_idx != n_left) { - temp = hash->arData[n_left]; - hash->arData[n_left] = hash->arData[rnd_idx]; - hash->arData[rnd_idx] = temp; + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + } + } + } else { + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + zend_hash_iterators_update(hash, idx, j); + } + j++; + } + } + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + zend_hash_iterators_update(hash, rnd_idx, n_left); + } } } - HANDLE_BLOCK_INTERRUPTIONS(); hash->nNumUsed = n_elems; hash->nInternalPointer = 0; @@ -2194,18 +2217,33 @@ PHP_FUNCTION(array_shift) if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) { uint32_t k = 0; - for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { - p = Z_ARRVAL_P(stack)->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (idx != k) { - Bucket *q = Z_ARRVAL_P(stack)->arData + k; - q->h = k; - q->key = NULL; - ZVAL_COPY_VALUE(&q->val, &p->val); - ZVAL_UNDEF(&p->val); - zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + } + k++; + } + } else { + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + } + k++; } - k++; } Z_ARRVAL_P(stack)->nNumUsed = k; Z_ARRVAL_P(stack)->nNextFreeElement = k; @@ -2258,21 +2296,46 @@ PHP_FUNCTION(array_unshift) } zend_hash_next_index_insert_new(&new_hash, &args[i]); } - 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(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 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); + } + } ZEND_HASH_FOREACH_END(); + } else { + uint32_t old_idx; + uint32_t new_idx = i; - new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount; + 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; + zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + new_idx++; + } ZEND_HASH_FOREACH_END(); + } + + /* replace HashTable data */ Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0; Z_ARRVAL_P(stack)->pDestructor = NULL; zend_hash_destroy(Z_ARRVAL_P(stack)); - *Z_ARRVAL_P(stack) = new_hash; + + Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags; + Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize; + Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask; + Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed; + Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements; + Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement; + Z_ARRVAL_P(stack)->arData = new_hash.arData; + Z_ARRVAL_P(stack)->arHash = new_hash.arHash; + Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor; + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); - zend_hash_iterators_reset(Z_ARRVAL_P(stack)); /* Clean up and return the number of elements in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt index 10f43f3998..ea728156f0 100644 --- a/tests/lang/foreachLoop.013.phpt +++ b/tests/lang/foreachLoop.013.phpt @@ -445,28 +445,12 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** --> State of array after loop: -array(7) { +array(2) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> - string(5) "new.1" - [5]=> string(5) "new.0" - [6]=> - string(3) "v.0" + [1]=> + &string(3) "v.0" } ---( Array with 2 element(s): )--- @@ -479,30 +463,17 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=2; $v=v.1 --> State of array after loop: -array(8) { +array(4) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> - string(3) "v.1" + [3]=> + &string(3) "v.1" } ---( Array with 3 element(s): )--- @@ -517,32 +488,19 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=3; $v=v.2 --> State of array after loop: -array(9) { +array(5) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> - string(3) "v.2" + [4]=> + &string(3) "v.2" } ---( Array with 4 element(s): )--- @@ -559,32 +517,19 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=4; $v=v.3 --> State of array after loop: -array(10) { +array(6) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> + [4]=> string(3) "v.2" - [9]=> - string(3) "v.3" + [5]=> + &string(3) "v.3" } diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt index a56249ee0b..a4ee09d6a9 100644 --- a/tests/lang/foreachLoop.015.phpt +++ b/tests/lang/foreachLoop.015.phpt @@ -447,28 +447,12 @@ array(1) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** --> State of array after loop: -array(7) { +array(2) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> - string(5) "new.1" - [5]=> string(5) "new.0" - [6]=> - string(3) "v.0" + [1]=> + &string(3) "v.0" } ---( Array with 2 element(s): )--- @@ -481,30 +465,17 @@ array(2) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=2; $v=v.1 --> State of array after loop: -array(8) { +array(4) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> - string(3) "v.1" + [3]=> + &string(3) "v.1" } ---( Array with 3 element(s): )--- @@ -519,32 +490,19 @@ array(3) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=3; $v=v.2 --> State of array after loop: -array(9) { +array(5) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> - string(3) "v.2" + [4]=> + &string(3) "v.2" } ---( Array with 4 element(s): )--- @@ -561,32 +519,19 @@ array(4) { } --> Do loop: iteration 0: $k=0; $v=v.0 - iteration 1: $k=0; $v=new.0 - iteration 2: $k=0; $v=new.1 - iteration 3: $k=0; $v=new.2 - iteration 4: $k=0; $v=new.3 - iteration 5: $k=0; $v=new.4 - ** Stuck in a loop! ** + iteration 1: $k=4; $v=v.3 --> State of array after loop: -array(10) { +array(6) { [0]=> - string(5) "new.5" - [1]=> - &string(5) "new.4" - [2]=> - string(5) "new.3" - [3]=> - string(5) "new.2" - [4]=> string(5) "new.1" - [5]=> + [1]=> string(5) "new.0" - [6]=> + [2]=> string(3) "v.0" - [7]=> + [3]=> string(3) "v.1" - [8]=> + [4]=> string(3) "v.2" - [9]=> - string(3) "v.3" + [5]=> + &string(3) "v.3" }