From e688e7fa2210d93e4a9b689859671efdfb470c9b Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 18 Jul 2024 20:24:31 -0400 Subject: [PATCH] add `serde` feature --- Cargo.toml | 8 +++- src/lib.rs | 3 ++ src/serde_impls.rs | 117 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/serde_impls.rs diff --git a/Cargo.toml b/Cargo.toml index 7b6f439..e35280d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,9 @@ readme = "README.md" exclude = ["assets/*"] [dependencies] -atomic-wait = "1.1.0" seize = "0.4.4" +atomic-wait = "1.1.0" +serde = { version = "1.0.204", optional = true } [dev-dependencies] rand = "0.8.5" @@ -22,6 +23,11 @@ dashmap = "5.5.3" criterion = "0.5.1" tokio = { version = "1.38.0", features = ["fs", "rt"] } num_cpus = "1.16.0" +serde_json = "1.0.120" + +[features] +default = [] +serde = ["dep:serde"] [profile.test] inherits = "release" diff --git a/src/lib.rs b/src/lib.rs index 06b226e..ab65187 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -223,6 +223,9 @@ The `Guard` trait supports both local and owned guards. Note the `'guard` lifeti mod map; mod raw; +#[cfg(feature = "serde")] +mod serde_impls; + pub use map::{ Compute, HashMap, HashMapBuilder, HashMapRef, Iter, Keys, OccupiedError, Operation, ResizeMode, Values, diff --git a/src/serde_impls.rs b/src/serde_impls.rs new file mode 100644 index 0000000..b16d80a --- /dev/null +++ b/src/serde_impls.rs @@ -0,0 +1,117 @@ +use serde::de::{MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use std::fmt::{self, Formatter}; +use std::hash::{BuildHasher, Hash}; +use std::marker::PhantomData; + +use crate::{Guard, HashMap, HashMapRef}; + +struct MapVisitor { + _marker: PhantomData>, +} + +impl Serialize for HashMapRef<'_, K, V, S, G> +where + K: Serialize + Hash + Eq, + V: Serialize, + G: Guard, + S: BuildHasher, +{ + fn serialize(&self, serializer: Sr) -> Result + where + Sr: Serializer, + { + serializer.collect_map(self) + } +} + +impl Serialize for HashMap +where + K: Serialize + Hash + Eq, + V: Serialize, + S: BuildHasher, +{ + fn serialize(&self, serializer: Sr) -> Result + where + Sr: Serializer, + { + self.pin().serialize(serializer) + } +} + +impl<'de, K, V, S> Deserialize<'de> for HashMap +where + K: Deserialize<'de> + Hash + Eq, + V: Deserialize<'de>, + S: Default + BuildHasher, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(MapVisitor::new()) + } +} + +impl MapVisitor { + pub(crate) fn new() -> Self { + Self { + _marker: PhantomData, + } + } +} + +impl<'de, K, V, S> Visitor<'de> for MapVisitor +where + K: Deserialize<'de> + Hash + Eq, + V: Deserialize<'de>, + S: Default + BuildHasher, +{ + type Value = HashMap; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "a map") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let values = match access.size_hint() { + Some(size) => HashMap::with_capacity_and_hasher(size, S::default()), + None => HashMap::default(), + }; + + { + let values = values.pin(); + while let Some((key, value)) = access.next_entry()? { + values.insert(key, value); + } + } + + Ok(values) + } +} + +#[cfg(test)] +mod test { + use crate::HashMap; + + #[test] + fn test_map() { + let map: HashMap = HashMap::new(); + let guard = map.guard(); + + map.insert(0, 4, &guard); + map.insert(1, 3, &guard); + map.insert(2, 2, &guard); + map.insert(3, 1, &guard); + map.insert(4, 0, &guard); + + let serialized = serde_json::to_string(&map).unwrap(); + let deserialized = serde_json::from_str(&serialized).unwrap(); + + assert_eq!(map, deserialized); + } +}