From 684e4c546d4f08fb15e983d6d67e55b821c2060a Mon Sep 17 00:00:00 2001 From: Avril Date: Thu, 22 Oct 2020 02:46:31 +0100 Subject: [PATCH] fixed serde implementation; added macro --- Cargo.toml | 5 +++- src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++-- src/page_impls.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++- src/tests.rs | 32 ++++++++++++++++++++ 4 files changed, 172 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6021dba..d517228 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ description = "Small byte-sized generic key-value map type" keywords = ["map", "table", "small", "key", "value"] repository = "https://github.com/notflan/smallmap" homepage= "https://git.flanchan.moe/flanchan/smallmap" -version = "1.1.5" +version = "1.1.6" authors = ["Avril "] edition = "2018" license = "MIT" @@ -12,5 +12,8 @@ license = "MIT" [dependencies] serde = {version = "1.0.116", features = ["derive"], optional = true} +[dev-dependencies] +serde_json = "1.0.59" + [build-dependencies] rustc_version = "0.2" diff --git a/src/lib.rs b/src/lib.rs index ef4fba4..9dc9014 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,43 @@ pub use entry::Entry; mod init; +/// A helper macro for creating `Map` instances with or without pre-set entries. +/// +/// # Create empty map +/// With no parameters this just calls `Map::new()`. +/// ``` +/// # use smallmap::*; +/// let map: Map = smallmap!(); +/// let map2: Map = Map::new(); +/// assert_eq!(map, map2); +/// ``` +/// # Create with key-value pairs +/// You can specify some entries to pre-insert in the format `{key => value}`. +/// ``` +/// # use smallmap::*; +/// let map = smallmap! { +/// {"Key" => 1}, +/// {"Key two" => 2}, +/// {"Key three" => 3}, +/// {"Key four" => 4}, +/// }; +/// ``` +#[macro_export ]macro_rules! smallmap { + () => { + $crate::Map::new() + }; + ($({$key:expr => $value:expr}),* $(,)?) => { + { + let mut map = $crate::Map::new(); + $( + map.insert($key, $value); + )* + map + } + } +} + + /// Trait for types that can be used as `Map` keys. /// /// Implementors should try to minimise collisions by making `collapse` return a relatively unique value if possible. @@ -79,7 +116,7 @@ where K: Collapse { Self(init::blank_page()) } - + /// The number of entries currently in this page /// /// This is a count that iterates over all slots, if possible store it in a temporary instead of re-calling it many times. @@ -143,7 +180,7 @@ where K: Collapse /// A small hashtable-like map with byte sized key indecies. #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -#[cfg_attr(feature="serde", derive(serde::Serialize))] +#[cfg_attr(feature="serde", derive(serde::Serialize, serde::Deserialize))] pub struct Map(Vec>); impl Map @@ -172,6 +209,8 @@ where K: Collapse } None } + + /// Get an `Entry` for the `key` that lets you get or insert the value pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { // somehow this is faster than using index, even though here we search twice????? i don't know why but there you go @@ -207,6 +246,11 @@ where K: Collapse { self.pages().map(Page::len).sum() } + /// Is this map empty + pub fn is_empty(&self) -> bool + { + self.0[0].iter().next().is_none() + } /// The number of pages currently in this map pub fn num_pages(&self) -> usize { @@ -250,8 +294,13 @@ where K: Collapse /// Create a new empty `Map` with a specific number of pages pre-allocated pub fn with_capacity(pages: usize) -> Self { + #[cold] fn cap_too_low() -> ! + { + panic!("Got 0 capacity, this is invalid.") + } + if pages == 0 { - panic!("Got 0 capacity, this is invalid."); + cap_too_low() } let mut p = Vec::with_capacity(pages); p.push(Page::new()); @@ -354,6 +403,17 @@ impl IntoIterator for Map } } +impl std::iter::Extend<(K,V)> for Map +{ + fn extend>(&mut self, iter: T) { + // we can probably optimise this better, right? + for (key, value) in iter.into_iter() + { + self.insert(key,value); + } + } +} + use std::hash::{Hash, Hasher,}; impl Collapse for T { diff --git a/src/page_impls.rs b/src/page_impls.rs index 64c5012..9842a07 100644 --- a/src/page_impls.rs +++ b/src/page_impls.rs @@ -49,6 +49,8 @@ impl hash::Hash for Page { #[cfg(feature="serde")] const _: () = { use serde::*; + use std::marker::PhantomData; + impl serde::Serialize for Page where K:Serialize, V: Serialize { @@ -56,7 +58,77 @@ const _: () = { where S: Serializer, { - serializer.serialize_slice(&self.0[..]) + use serde::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(Some(MAX))?; + for element in self.0.iter() { + seq.serialize_element(element)?; + } + seq.end() } } + + struct PageVisitor(PhantomData>); + + impl<'de, K, V> de::Visitor<'de> for PageVisitor + where K: Deserialize<'de>, + V: Deserialize<'de> + { + type Value = Page; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an array of 256 `Option<(K,V)>` elements") + } + + fn visit_seq(self, mut seq: A) -> Result where + A: serde::de::SeqAccess<'de> + { + let mut elems = init::blank_page(); + let mut i=0usize; + while let Some(optkv) = seq.next_element()? + { + elems[i] = optkv; + i+=1; + } + if i==MAX { + Ok(Page(elems)) + } else { + use serde::de::Error; + Err(A::Error::custom(format!("Expected {} elemts, got {}", MAX, i))) + } + } + } + + + impl<'de, K, V> Deserialize<'de> for Page + where K: Deserialize<'de>, + V: Deserialize<'de> + { + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: serde::de::Deserializer<'de>, + { + deserializer.deserialize_seq(PageVisitor(PhantomData)) + } + } + }; + + +#[cfg(feature="serde")] +#[cfg(test)] +mod serde_tests +{ + use crate::*; + #[test] + fn serde() + { + let map = smallmap! { + {101 => 102}, + {10111 => 10222}, + }; + let string = serde_json::to_string(&map).expect("ser failed"); + println!("String {:?}", string); + let map2 = serde_json::from_str(&string[..]).expect("de failed"); + assert_eq!(map, map2); + } +} diff --git a/src/tests.rs b/src/tests.rs index 6df4130..b5022ce 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -4,6 +4,38 @@ use std::collections::{ HashMap, }; +#[test] +fn macros() +{ + let map1 = smallmap!{ + {"One" => 1}, + {"Two" => 2}, + {"Four" => 40} + }; + let map2 = { + let mut map = Map::new(); + map.insert("One", 1); + map.insert("Two", 2); + map.insert("Four", 40); + map + }; + assert_eq!(map1.len(), 3); + assert_eq!(map1,map2); +} + +#[test] +fn is_empty() +{ + let map1 = smallmap!{ + {"One" => 1}, + {"Two" => 2}, + {"Four" => 40} + }; + let map2: Map<(),()> = Map::new(); + assert!(!map1.is_empty()); + assert!(map2.is_empty()); +} + #[test] fn it_works() {