Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #70 from KeithJRome/wi_32527_dup_key_in_dict

Fix for "Duplicate key in dict" issue
  • Loading branch information...
commit 52cbfff136b7a677fc32f25797a4cff696c59a14 2 parents 4e1093b + 5e96203
@KeithJRome KeithJRome authored
View
32 Languages/IronPython/IronPython/Runtime/CommonDictionaryStorage.cs
@@ -315,25 +315,47 @@ public CommonDictionaryStorage(object[] items, bool isHomogeneous)
Debug.Assert(key != null);
Debug.Assert(_count < buckets.Length);
- int index = hc % buckets.Length;
+ int startIndex = hc % buckets.Length;
+ // scan forward for matching key first
+ int index = startIndex;
+ int firstUsableIndex = -1;
for (; ; ) {
Bucket cur = buckets[index];
- if (cur.Key == null || cur.Key == _removed) {
+ if (cur.Key == null) {
+ // no entry was ever here, nothing more to probe
+ if (firstUsableIndex == -1) {
+ firstUsableIndex = index;
+ }
break;
+ } else if (cur.Key == _removed) {
+ // we recycled this bucket, so need to continue walking to see if a following bucket matches
+ if (firstUsableIndex == -1) {
+ // retain the index of the first recycled bucket, in case we need it later
+ firstUsableIndex = index;
+ }
} else if (Object.ReferenceEquals(key, cur.Key) || (cur.HashCode == hc && _eqFunc(key, cur.Key))) {
+ // this bucket is a key match
_version++;
buckets[index].Value = value;
return false;
}
+ // keep walking
index = ProbeNext(buckets, index);
+
+ // if we ended up doing a full scan, then this means the key is not already in use and there are
+ // only recycled buckets available -- nothing more to probe
+ if (index == startIndex) {
+ break;
+ }
}
+ // the key wasn't found, but we did find a fresh or recycled (unused) bucket
_version++;
- buckets[index].HashCode = hc;
- buckets[index].Value = value;
- buckets[index].Key = key;
+ buckets[firstUsableIndex].HashCode = hc;
+ buckets[firstUsableIndex].Value = value;
+ buckets[firstUsableIndex].Key = key;
return true;
}
View
18 Languages/IronPython/Tests/test_dict.py
@@ -1172,4 +1172,22 @@ class C:
abc = {x:x for x in t if x == v}
AreEqual(abc, {2:2})
+def test_cp32527():
+ '''test for duplicate key in dict under specific hash value conditions'''
+ d = {'1': 1, '2': 1, '3': 1, 'a7': 1, 'a8': 1}
+ #d now has 7 buckets internally, and computed hash for a7 and a8 keys will land on same starting bucket index
+
+ #recycle the a7 bucket
+ d.pop('a7')
+
+ #attempt to update the a8 bucket, which now comes after the recycled a7
+ d['a8'] = 5
+
+ #if working properly, there will now be a recycled bucket (former home of a7) and a single a8 bucket
+ #if not working properly, there will instead be two a8 buckets
+ expected = 1
+ actual = d.keys().count('a8')
+ AreEqual(actual, expected)
+
+
run_test(__name__)
Please sign in to comment.
Something went wrong with that request. Please try again.