com php-src: Fixed bug #74361: NEWS ext/standard/array.c ext/standard/tests/array/bug74361.phpt ext/standard/tests/array /bug74361_2.phpt

[email protected] (Nikita Popov)
Newsgroups php.cvs
Message-ID <[email protected]>
Commit:    c8034514edadbafc4376f107e2a4ba52b7b17ff4
Author:    Nikita Popov <[email protected]>         Sun, 2 Apr 2017 13:19:32 +0200
Parents:   32dcc1eef037da88ff483dd49a5a153096d702fc
Branches:  PHP-7.1 master

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

Log:
Fixed bug #74361

Bugs:
https://bugs.php.net/74361

Changed paths:
  M  NEWS
  M  ext/standard/array.c
  A  ext/standard/tests/array/bug74361.phpt
  A  ext/standard/tests/array/bug74361_2.phpt


Diff:
diff --git a/NEWS b/NEWS
index a655dd3..635a51b 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,9 @@ PHP                                                                        NEWS
   . Fixed bug #74341 (openssl_x509_parse fails to parse ASN.1 UTCTime without
     seconds). (Moritz Fain)
 
+- Standard:
+  . Fixed bug #74361 (Compaction in array_rand() violates COW). (Nikita)
+
 13 Apr 2017, PHP 7.1.4
 
 - Core:
diff --git a/ext/standard/array.c b/ext/standard/array.c
index 0dd0f3a..966e0a3 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -5016,19 +5016,26 @@ PHP_FUNCTION(array_rand)
 	if (num_req == 1) {
 		HashTable *ht = Z_ARRVAL_P(input);
 
-		/* Compact the hashtable if less than 3/4 of elements are used */
-		if (num_avail < ht->nNumUsed - (ht->nNumUsed>>2)) {
-			if (ht->u.flags & HASH_FLAG_PACKED) {
-				zend_hash_packed_to_hash(ht);
-			} else {
-				zend_hash_rehash(ht);
-			}
+		if (num_avail < ht->nNumUsed - (ht->nNumUsed>>1)) {
+			/* If less than 1/2 of elements are used, don't sample. Instead search for a
+			 * specific offset using linear scan. */
+			zend_long i = 0, randval = php_mt_rand_range(0, num_avail - 1);
+			ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
+				if (i == randval) {
+					if (string_key) {
+						RETURN_STR_COPY(string_key);
+					} else {
+						RETURN_LONG(num_key);
+					}
+				}
+				i++;
+			} ZEND_HASH_FOREACH_END();
 		}
 
 		/* Sample random buckets until we hit one that is not empty.
-		 * The worst case probability of hitting an empty element is 1-3/4. The worst case
-		 * probability of hitting N empty elements in a row is (1-3/4)**N.
-		 * For N=5 this becomes smaller than 0.1%. */
+		 * The worst case probability of hitting an empty element is 1-1/2. The worst case
+		 * probability of hitting N empty elements in a row is (1-1/2)**N.
+		 * For N=10 this becomes smaller than 0.1%. */
 		do {
 			zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1);
 			Bucket *bucket = &ht->arData[randval];
diff --git a/ext/standard/tests/array/bug74361.phpt b/ext/standard/tests/array/bug74361.phpt
new file mode 100644
index 0000000..6e74590
--- /dev/null
+++ b/ext/standard/tests/array/bug74361.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Bug #74361: Compaction in array_rand() violates COW
+--FILE--
+<?php
+
+$array = [4 => 4];
+var_dump(array_rand($array));
+
+?>
+--EXPECT--
+int(4)
diff --git a/ext/standard/tests/array/bug74361_2.phpt b/ext/standard/tests/array/bug74361_2.phpt
new file mode 100644
index 0000000..4f4bdcf
--- /dev/null
+++ b/ext/standard/tests/array/bug74361_2.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Bug #74361: Compaction in array_rand() violates COW (variation)
+--FILE--
+<?php
+
+$array = range(0, 15);
+for ($i = 0; $i <= 8; $i++) {
+    unset($array[$i]);
+}
+
+foreach ($array as $x) {
+    var_dump($x);
+    array_rand($array, 1);
+}
+
+?>
+--EXPECT--
+int(9)
+int(10)
+int(11)
+int(12)
+int(13)
+int(14)
+int(15)
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.