Browse Source

fixed serde implementation; added macro

master
Avril 2 years ago
parent
commit
684e4c546d
Signed by: flanchan
GPG Key ID: 284488987C31F630
  1. 5
      Cargo.toml
  2. 66
      src/lib.rs
  3. 74
      src/page_impls.rs
  4. 32
      src/tests.rs

5
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 <flanchan@cumallover.me>"]
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"

66
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<i32, i32> = smallmap!();
/// let map2: Map<i32, i32> = 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<TKey, TValue>(Vec<Page<TKey,TValue>>);
impl<K,V> Map<K,V>
@ -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<K: Collapse, V> IntoIterator for Map<K,V>
}
}
impl<K: Collapse, V> std::iter::Extend<(K,V)> for Map<K,V>
{
fn extend<T: IntoIterator<Item = (K,V)>>(&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<T: Hash+ Eq> Collapse for T
{

74
src/page_impls.rs

@ -49,6 +49,8 @@ impl<K: hash::Hash, V: hash::Hash> hash::Hash for Page<K,V> {
#[cfg(feature="serde")]
const _: () = {
use serde::*;
use std::marker::PhantomData;
impl<K,V> serde::Serialize for Page<K,V>
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<K,V>(PhantomData<Page<K,V>>);
impl<'de, K, V> de::Visitor<'de> for PageVisitor<K,V>
where K: Deserialize<'de>,
V: Deserialize<'de>
{
type Value = Page<K,V>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an array of 256 `Option<(K,V)>` elements")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 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<K,V>
where K: Deserialize<'de>,
V: Deserialize<'de>
{
fn deserialize<D>(deserializer: D) -> Result<Page<K,V>, 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);
}
}

32
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()
{

Loading…
Cancel
Save