From: Nicholas Clark <nick@ccl4.org>
Date: Sun, 28 May 2006 10:40:48 +0000 (+0000)
Subject: Perl_refcounted_he_chain_2hv()'s code to skip duplicate keys was far
X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=9f76984545c78a615ef3f2522fb5b77578e41023;p=p5sagit%2Fp5-mst-13.2.git

Perl_refcounted_he_chain_2hv()'s code to skip duplicate keys was far
too lax.

p4raw-id: //depot/perl@28320
---

diff --git a/hv.c b/hv.c
index 04439fa..750988c 100644
--- a/hv.c
+++ b/hv.c
@@ -2634,7 +2634,26 @@ Perl_refcounted_he_chain_2hv(pTHX_ const struct refcounted_he *chain)
 
 	for (; entry; entry = HeNEXT(entry)) {
 	    if (HeHASH(entry) == hash) {
-		goto next_please;
+		/* We might have a duplicate key here.  If so, entry is older
+		   than the key we've already put in the hash, so if they are
+		   the same, skip adding entry.  */
+#ifdef USE_ITHREADS
+		const STRLEN klen = HeKLEN(entry);
+		const char *const key = HeKEY(entry);
+		if (klen == chain->refcounted_he_keylen
+		    && (!!HeKUTF8(entry)
+			== !!(chain->refcounted_he_data[0] & HVhek_UTF8))
+		    && memEQ(key, REF_HE_KEY(chain), klen))
+		    goto next_please;
+#else
+		if (HeKEY_hek(entry) == chain->refcounted_he_hek)
+		    goto next_please;
+		if (HeKLEN(entry) == HEK_LEN(chain->refcounted_he_hek)
+		    && HeKUTF8(entry) == HEK_UTF8(chain->refcounted_he_hek)
+		    && memEQ(HeKEY(entry), HEK_KEY(chain->refcounted_he_hek),
+			     HeKLEN(entry)))
+		    goto next_please;
+#endif
 	    }
 	}
 	assert (!entry);
diff --git a/t/op/caller.t b/t/op/caller.t
index d0716be..c5bb84e 100644
--- a/t/op/caller.t
+++ b/t/op/caller.t
@@ -5,7 +5,7 @@ BEGIN {
     chdir 't' if -d 't';
     @INC = '../lib';
     require './test.pl';
-    plan( tests => 71 );
+    plan( tests => 77 );
 }
 
 my @c;
@@ -267,3 +267,27 @@ EOE
     is(get_hash()->{$k2}, 2, "UTF-8 or not, it's the same");
     is(get_hash()->{$k3}, 3, "Octect sequences and UTF-8 are distinct");
 }
+
+{
+    my ($k1, $k2, $k3);
+    BEGIN {
+	($k1, $k2, $k3) = ("\0", "\0\0", "\0\0\0");
+	$^H{$k1} = 1;
+	$^H{$k2} = 2;
+	$^H{$k3} = 3;
+    }
+
+    is(get_hash()->{$k1}, 1, "Keys with the same hash value don't clash");
+    is(get_hash()->{$k2}, 2, "Keys with the same hash value don't clash");
+    is(get_hash()->{$k3}, 3, "Keys with the same hash value don't clash");
+
+    BEGIN {
+	$^H{$k1} = "a";
+	$^H{$k2} = "b";
+	$^H{$k3} = "c";
+    }
+
+    is(get_hash()->{$k1}, "a", "Keys with the same hash value don't clash");
+    is(get_hash()->{$k2}, "b", "Keys with the same hash value don't clash");
+    is(get_hash()->{$k3}, "c", "Keys with the same hash value don't clash");
+}