about summary refs log tree commit diff
diff options
context:
space:
mode:
authorKonrad Borowski <konrad@borowski.pw>2021-01-18 17:56:06 +0100
committerKonrad Borowski <konrad@borowski.pw>2021-01-18 17:56:06 +0100
commitae3a5153377f2271ba7dfe686a9b5bca1632c32b (patch)
treebfeb06c3979a4d0e464b5923fa9f5de2ba1ac461
parent66eb9821666e0672a69a998d2331733c7a8996a5 (diff)
downloadrust-ae3a5153377f2271ba7dfe686a9b5bca1632c32b.tar.gz
rust-ae3a5153377f2271ba7dfe686a9b5bca1632c32b.zip
Avoid hash_slice in VecDeque's Hash implementation
Fixes #80303.
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs10
-rw-r--r--library/alloc/src/collections/vec_deque/tests.rs40
2 files changed, 47 insertions, 3 deletions
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 5b61e8911a5..6f9133e2811 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -2646,9 +2646,13 @@ impl<A: Ord> Ord for VecDeque<A> {
 impl<A: Hash> Hash for VecDeque<A> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.len().hash(state);
-        let (a, b) = self.as_slices();
-        Hash::hash_slice(a, state);
-        Hash::hash_slice(b, state);
+        // It's not possible to use Hash::hash_slice on slices
+        // returned by as_slices method as their length can vary
+        // in otherwise identical deques.
+        //
+        // Hasher only guarantees equivalence for the exact same
+        // set of calls to its methods.
+        self.iter().for_each(|elem| elem.hash(state));
     }
 }
 
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index 27dc59ae644..87e06fa394d 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -599,3 +599,43 @@ fn issue_53529() {
         assert_eq!(*a, 2);
     }
 }
+
+#[test]
+fn issue_80303() {
+    use core::iter;
+    use core::num::Wrapping;
+
+    // This is a valid, albeit rather bad hash function implementation.
+    struct SimpleHasher(Wrapping<u64>);
+
+    impl Hasher for SimpleHasher {
+        fn finish(&self) -> u64 {
+            self.0.0
+        }
+
+        fn write(&mut self, bytes: &[u8]) {
+            // This particular implementation hashes value 24 in addition to bytes.
+            // Such an implementation is valid as Hasher only guarantees equivalence
+            // for the exact same set of calls to its methods.
+            for &v in iter::once(&24).chain(bytes) {
+                self.0 = Wrapping(31) * self.0 + Wrapping(u64::from(v));
+            }
+        }
+    }
+
+    fn hash_code(value: impl Hash) -> u64 {
+        let mut hasher = SimpleHasher(Wrapping(1));
+        value.hash(&mut hasher);
+        hasher.finish()
+    }
+
+    // This creates two deques for which values returned by as_slices
+    // method differ.
+    let vda: VecDeque<u8> = (0..10).collect();
+    let mut vdb = VecDeque::with_capacity(10);
+    vdb.extend(5..10);
+    (0..5).rev().for_each(|elem| vdb.push_front(elem));
+    assert_ne!(vda.as_slices(), vdb.as_slices());
+    assert_eq!(vda, vdb);
+    assert_eq!(hash_code(vda), hash_code(vdb));
+}