From 6df19ae326d5e14c44820c83af956dd35999ecb8 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 15:37:15 +0100 Subject: [PATCH 1/7] Implement Eq and PartialEq for FlurryHashMap --- src/lib.rs | 22 ++++++++++++++++++++++ tests/basic.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a30df88c..3638e99b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1253,6 +1253,28 @@ where } } +impl PartialEq for FlurryHashMap +where + K: Sync + Send + Clone + Eq + Hash, + V: Sync + Send + PartialEq, + S: BuildHasher, +{ + fn eq(&self, other: &Self) -> bool { + let guard = epoch::pin(); + + self.iter(&guard) + .all(|(key, value)| other.get(key, &guard).map_or(false, |v| *value == *v)) + } +} + +impl Eq for FlurryHashMap +where + K: Sync + Send + Clone + Eq + Hash, + V: Sync + Send + PartialEq, + S: BuildHasher, +{ +} + impl Drop for FlurryHashMap { fn drop(&mut self) { // safety: we have &mut self _and_ all references we have returned are bound to the diff --git a/tests/basic.rs b/tests/basic.rs index 36d7dc8c..054fcfab 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -162,6 +162,51 @@ fn current_kv_dropped() { assert_eq!(Arc::strong_count(&dropped2), 1); } +#[test] +fn empty_maps_equal() { + let map1 = FlurryHashMap::::new(); + let map2 = FlurryHashMap::::new(); + assert_eq!(map1, map2); +} + +#[test] +fn different_size_maps_not_equal() { + let map1 = FlurryHashMap::::new(); + let map2 = FlurryHashMap::::new(); + { + let guard = epoch::pin(); + map1.insert(1, 0, &guard); + } + + assert_ne!(map1, map2); +} + +#[test] +fn same_values_equal() { + let map1 = FlurryHashMap::::new(); + let map2 = FlurryHashMap::::new(); + { + let guard = epoch::pin(); + map1.insert(1, 0, &guard); + map2.insert(1, 0, &guard); + } + + assert_eq!(map1, map2); +} + +#[test] +fn different_values_not_equal() { + let map1 = FlurryHashMap::::new(); + let map2 = FlurryHashMap::::new(); + { + let guard = epoch::pin(); + map1.insert(1, 0, &guard); + map2.insert(1, 1, &guard); + } + + assert_ne!(map1, map2); +} + #[test] #[ignore] // ignored because we cannot control when destructors run From 3842a471f69a47e552a82219f53b38c635dccf49 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 16:23:39 +0100 Subject: [PATCH 2/7] Change V to Eq in Eq impl, check equality in tests both ways, add values to both maps in different size test --- src/lib.rs | 7 ++++++- tests/basic.rs | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3638e99b..c1666d8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -200,11 +200,16 @@ use node::*; use crossbeam::epoch::{Atomic, Guard, Owned, Shared}; use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash}; +<<<<<<< HEAD use std::iter::FromIterator; use std::sync::{ atomic::{AtomicIsize, AtomicUsize, Ordering}, Once, }; +======= +use std::ops::Index; +use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering}; +>>>>>>> Change V to Eq in Eq impl, check equality in tests both ways, add values to both maps in different size test /// The largest possible table capacity. This value must be /// exactly 1<<30 to stay within Java array allocation and indexing @@ -1270,7 +1275,7 @@ where impl Eq for FlurryHashMap where K: Sync + Send + Clone + Eq + Hash, - V: Sync + Send + PartialEq, + V: Sync + Send + Eq, S: BuildHasher, { } diff --git a/tests/basic.rs b/tests/basic.rs index 054fcfab..19c5692a 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -167,6 +167,7 @@ fn empty_maps_equal() { let map1 = FlurryHashMap::::new(); let map2 = FlurryHashMap::::new(); assert_eq!(map1, map2); + assert_eq!(map2, map1); } #[test] @@ -176,9 +177,12 @@ fn different_size_maps_not_equal() { { let guard = epoch::pin(); map1.insert(1, 0, &guard); + map1.insert(2, 0, &guard); + map2.insert(1, 0, &guard); } assert_ne!(map1, map2); + assert_ne!(map2, map1); } #[test] @@ -192,6 +196,7 @@ fn same_values_equal() { } assert_eq!(map1, map2); + assert_eq!(map2, map1); } #[test] @@ -205,6 +210,7 @@ fn different_values_not_equal() { } assert_ne!(map1, map2); + assert_ne!(map2, map1); } #[test] From 5ba38b0efb384035b47eb42878c4cfcc71468dff Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 16:36:48 +0100 Subject: [PATCH 3/7] Implement Index trait for FlurryHashMap --- src/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c1666d8e..0c3ff379 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1280,6 +1280,20 @@ where { } +impl Index for FlurryHashMap +where + K: Sync + Send + Clone + Eq + Hash, + V: Sync + Send, + S: BuildHasher, +{ + type Output = V; + + fn index(&self, idx: K) -> &Self::Output { + let guard = epoch::pin(); + self.get(&idx, &guard).expect("no entry found for key") + } +} + impl Drop for FlurryHashMap { fn drop(&mut self) { // safety: we have &mut self _and_ all references we have returned are bound to the From 2d03fbcb473af488f76308725ef9dcd92507f145 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 16:52:13 +0100 Subject: [PATCH 4/7] Add len check to PartialEq impl --- src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0c3ff379..9860d69e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -200,16 +200,12 @@ use node::*; use crossbeam::epoch::{Atomic, Guard, Owned, Shared}; use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash}; -<<<<<<< HEAD use std::iter::FromIterator; +use std::ops::Index; use std::sync::{ atomic::{AtomicIsize, AtomicUsize, Ordering}, Once, }; -======= -use std::ops::Index; -use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering}; ->>>>>>> Change V to Eq in Eq impl, check equality in tests both ways, add values to both maps in different size test /// The largest possible table capacity. This value must be /// exactly 1<<30 to stay within Java array allocation and indexing @@ -1265,8 +1261,11 @@ where S: BuildHasher, { fn eq(&self, other: &Self) -> bool { - let guard = epoch::pin(); + if self.len() != other.len() { + return false; + } + let guard = epoch::pin(); self.iter(&guard) .all(|(key, value)| other.get(key, &guard).map_or(false, |v| *value == *v)) } From 548a70d07bfd74bbd26ae2138f7ed1f7f6ea11dc Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 19:02:51 +0100 Subject: [PATCH 5/7] Remove index implementation because it's currently impossible to implement it --- src/lib.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9860d69e..0955fb92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -201,7 +201,6 @@ use crossbeam::epoch::{Atomic, Guard, Owned, Shared}; use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash}; use std::iter::FromIterator; -use std::ops::Index; use std::sync::{ atomic::{AtomicIsize, AtomicUsize, Ordering}, Once, @@ -1279,20 +1278,6 @@ where { } -impl Index for FlurryHashMap -where - K: Sync + Send + Clone + Eq + Hash, - V: Sync + Send, - S: BuildHasher, -{ - type Output = V; - - fn index(&self, idx: K) -> &Self::Output { - let guard = epoch::pin(); - self.get(&idx, &guard).expect("no entry found for key") - } -} - impl Drop for FlurryHashMap { fn drop(&mut self) { // safety: we have &mut self _and_ all references we have returned are bound to the From 5d452e587911d8b98fcd9e178de905e04c14f0e1 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 19:15:11 +0100 Subject: [PATCH 6/7] Implement Debug trait --- src/lib.rs | 14 +++++++++++++- tests/basic.rs | 12 ++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0955fb92..173816a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -199,6 +199,7 @@ use node::*; use crossbeam::epoch::{Atomic, Guard, Owned, Shared}; use std::collections::hash_map::RandomState; +use std::fmt::{self, Debug, Formatter}; use std::hash::{BuildHasher, Hash}; use std::iter::FromIterator; use std::sync::{ @@ -257,7 +258,6 @@ pub mod epoch { /// A concurrent hash table. /// /// See the [crate-level documentation](index.html) for details. -#[derive(Debug)] pub struct FlurryHashMap { /// The array of bins. Lazily initialized upon first insertion. /// Size is always a power of two. Accessed directly by iterators. @@ -1278,6 +1278,18 @@ where { } +impl fmt::Debug for FlurryHashMap +where + K: Sync + Send + Clone + Debug + Eq + Hash, + V: Sync + Send + Debug, + S: BuildHasher, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let guard = epoch::pin(); + f.debug_map().entries(self.iter(&guard)).finish() + } +} + impl Drop for FlurryHashMap { fn drop(&mut self) { // safety: we have &mut self _and_ all references we have returned are bound to the diff --git a/tests/basic.rs b/tests/basic.rs index 19c5692a..18a7d9cf 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -7,6 +7,18 @@ fn new() { let _map = FlurryHashMap::::new(); } +#[test] +fn debug_format() { + let map = FlurryHashMap::::new(); + { + let g = epoch::pin(); + map.insert(1, 0, &g); + map.insert(2, 1, &g); + map.insert(3, 2, &g); + map.insert(4, 3, &g); + } + assert_eq!("{3: 2, 1: 0, 2: 1, 4: 3}", format!("{:?}", map)); +} #[test] fn insert() { let map = FlurryHashMap::::new(); From 6304ae6b4c3ace2043fea8e387e2079901062c77 Mon Sep 17 00:00:00 2001 From: Justus K Date: Fri, 24 Jan 2020 19:19:59 +0100 Subject: [PATCH 7/7] Remove debug_format test because the debug output is in random order --- tests/basic.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/basic.rs b/tests/basic.rs index 18a7d9cf..19c5692a 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -7,18 +7,6 @@ fn new() { let _map = FlurryHashMap::::new(); } -#[test] -fn debug_format() { - let map = FlurryHashMap::::new(); - { - let g = epoch::pin(); - map.insert(1, 0, &g); - map.insert(2, 1, &g); - map.insert(3, 2, &g); - map.insert(4, 3, &g); - } - assert_eq!("{3: 2, 1: 0, 2: 1, 4: 3}", format!("{:?}", map)); -} #[test] fn insert() { let map = FlurryHashMap::::new();