From 18de104d385d5d91ea7c1dc7dbdf22bebad0e642 Mon Sep 17 00:00:00 2001 From: Bronson Philippa Date: Mon, 28 Mar 2022 20:19:33 +1000 Subject: [PATCH] Extended Encode and Decode for HashMap and HashSet to support custom hashers (#529) --- src/features/impl_std.rs | 16 ++++++++++------ tests/std.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/features/impl_std.rs b/src/features/impl_std.rs index c923001c..0f282982 100644 --- a/src/features/impl_std.rs +++ b/src/features/impl_std.rs @@ -361,7 +361,7 @@ impl Decode for SocketAddrV6 { impl std::error::Error for EncodeError {} impl std::error::Error for DecodeError {} -impl Encode for HashMap +impl Encode for HashMap where K: Encode, V: Encode, @@ -376,16 +376,18 @@ where } } -impl Decode for HashMap +impl Decode for HashMap where K: Decode + Eq + std::hash::Hash, V: Decode, + S: std::hash::BuildHasher + Default, { fn decode(decoder: &mut D) -> Result { let len = crate::de::decode_slice_len(decoder)?; decoder.claim_container_read::<(K, V)>(len)?; - let mut map = HashMap::with_capacity(len); + let hash_builder: S = Default::default(); + let mut map = HashMap::with_capacity_and_hasher(len, hash_builder); for _ in 0..len { // See the documentation on `unclaim_bytes_read` as to why we're doing this here decoder.unclaim_bytes_read(core::mem::size_of::<(K, V)>()); @@ -398,15 +400,17 @@ where } } -impl Decode for HashSet +impl Decode for HashSet where T: Decode + Eq + Hash, + S: std::hash::BuildHasher + Default, { fn decode(decoder: &mut D) -> Result { let len = crate::de::decode_slice_len(decoder)?; decoder.claim_container_read::(len)?; - let mut map = HashSet::new(); + let hash_builder: S = Default::default(); + let mut map: HashSet = HashSet::with_capacity_and_hasher(len, hash_builder); for _ in 0..len { // See the documentation on `unclaim_bytes_read` as to why we're doing this here decoder.unclaim_bytes_read(core::mem::size_of::()); @@ -418,7 +422,7 @@ where } } -impl Encode for HashSet +impl Encode for HashSet where T: Encode, { diff --git a/tests/std.rs b/tests/std.rs index 658a4211..e4a392d0 100644 --- a/tests/std.rs +++ b/tests/std.rs @@ -106,6 +106,20 @@ fn test_std_commons() { set.insert("World".to_string()); the_same(set); + // HashMap and HashSet with custom hash algorithm + type MyBuildHasher = std::hash::BuildHasherDefault; + let mut custom_map: std::collections::HashMap = + Default::default(); + custom_map.insert("Hello".to_owned(), "world".to_owned()); + custom_map.insert("How".to_owned(), "are".to_owned()); + custom_map.insert("you".to_owned(), "doing?".to_owned()); + the_same(custom_map); + + let mut custom_set: std::collections::HashSet = Default::default(); + custom_set.insert("Hello".to_string()); + custom_set.insert("World".to_string()); + the_same(custom_set); + // Borrowed values let config = bincode::config::standard(); let mut buffer = [0u8; 1024]; @@ -141,3 +155,21 @@ fn test_system_time_out_of_range() { } ); } + +/// Simple example of user-defined hasher to test encoding/decoding HashMap and HashSet with custom hash algorithms. +#[derive(Copy, Clone, Default)] +pub struct ExampleCustomHasher { + pub hash: u64, +} + +impl std::hash::Hasher for ExampleCustomHasher { + fn write(&mut self, value: &[u8]) { + for (index, &item) in value.iter().enumerate() { + self.hash ^= u64::from(item) << ((index % 8) * 8); + } + } + + fn finish(&self) -> u64 { + self.hash + } +}