#![feature(hash_raw_entry)]
#![feature(test)]
use std::borrow::Borrow;
use std::collections::hash_map::{
HashMap,
RawEntryMut,
};
use std::hash::{Hash, Hasher, BuildHasher};
pub trait EntryAPI<K, Q, V, S>
where
K: Borrow<Q>,
Q: ToOwned<Owned = K> + ?Sized,
{
fn entry_ownable<'a, 'q>(&'a mut self, key: &'q Q) -> Entry<'a, 'q, K, Q, V, S>;
}
impl<K, Q, V, S> EntryAPI<K, Q, V, S> for HashMap<K, V, S>
where
K: Borrow<Q> + Hash + Eq,
Q: ToOwned<Owned = K> + Hash + Eq + ?Sized,
S: BuildHasher
{
#[inline]
fn entry_ownable<'a, 'q>(&'a mut self, key: &'q Q) -> Entry<'a, 'q, K, Q, V, S> {
let mut hasher = self.hasher().build_hasher();
key.hash(&mut hasher);
let hash = hasher.finish();
Entry {
key,
hash,
raw: self.raw_entry_mut().from_key_hashed_nocheck(hash, key),
}
}
}
pub struct Entry<'a, 'q, K, Q, V, S>
where
K: Borrow<Q>,
Q: ToOwned<Owned = K> + ?Sized
{
key: &'q Q,
hash: u64,
raw: RawEntryMut<'a, K, V, S>,
}
impl<'a, 'q, K, Q, V, S> Entry<'a, 'q, K, Q, V, S>
where
K: Borrow<Q> + Hash,
Q: ToOwned<Owned = K> + ?Sized,
S: BuildHasher
{
#[inline]
pub fn or_insert(self, default: V) -> &'a mut V {
match self.raw {
RawEntryMut::Occupied(e) =>
e.into_mut(),
RawEntryMut::Vacant(e) =>
e.insert_hashed_nocheck(self.hash, self.key.to_owned(), default).1,
}
}
#[inline]
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self.raw {
RawEntryMut::Occupied(e) =>
e.into_mut(),
RawEntryMut::Vacant(e) =>
e.insert_hashed_nocheck(self.hash, self.key.to_owned(), default()).1,
}
}
#[inline]
pub fn and_modify<F>(mut self, f: F) -> Self
where F: FnOnce(&mut V)
{
match self.raw {
RawEntryMut::Occupied(ref mut e) =>
f(e.get_mut()),
RawEntryMut::Vacant(_) =>
(),
}
self
}
}
impl<'a, 'q, K, Q, V, S> Entry<'a, 'q, K, Q, V, S>
where
K: Borrow<Q> + Hash,
Q: ToOwned<Owned = K> + ?Sized,
V: Default,
S: BuildHasher
{
#[inline]
pub fn or_default(self) -> &'a mut V {
match self.raw {
RawEntryMut::Occupied(e) =>
e.into_mut(),
RawEntryMut::Vacant(e) =>
e.insert_hashed_nocheck(self.hash, self.key.to_owned(), Default::default()).1,
}
}
}
#[cfg(test)]
mod silly_bench {
extern crate test;
use test::Bencher;
use std::collections::HashMap;
use super::EntryAPI;
fn data() -> Vec<String> {
(1..8192)
.map(|n| format!("{:013b}", n))
.collect()
}
fn entry(b: &mut Bencher, n: usize) {
let data = data();
let data: Vec<&str> = data.iter().map(|s| s.as_str()).collect();
b.iter(|| {
let mut map: HashMap<String, _> = HashMap::with_capacity(data.len() * 2);
for _ in 0..n {
for &i in &data {
let counter = map.entry(i.to_string()).or_insert(0);
*counter += 1;
}
}
})
}
#[bench] fn entry_1(b: &mut Bencher) { entry(b, 1) }
#[bench] fn entry_2(b: &mut Bencher) { entry(b, 2) }
#[bench] fn entry_4(b: &mut Bencher) { entry(b, 4) }
#[bench] fn entry_8(b: &mut Bencher) { entry(b, 8) }
fn entry_ownable(b: &mut Bencher, n: usize) {
let data = data();
let data: Vec<&str> = data.iter().map(|s| s.as_str()).collect();
b.iter(|| {
let mut map: HashMap<String, _> = HashMap::with_capacity(data.len() * 2);
for _ in 0..n {
for &i in &data {
let counter = map.entry_ownable(i).or_insert(0);
*counter += 1;
}
}
})
}
#[bench] fn entry_ownable_1(b: &mut Bencher) { entry_ownable(b, 1) }
#[bench] fn entry_ownable_2(b: &mut Bencher) { entry_ownable(b, 2) }
#[bench] fn entry_ownable_4(b: &mut Bencher) { entry_ownable(b, 4) }
#[bench] fn entry_ownable_8(b: &mut Bencher) { entry_ownable(b, 8) }
fn get_or_insert(b: &mut Bencher, n: usize) {
let data = data();
let data: Vec<&str> = data.iter().map(|s| s.as_str()).collect();
b.iter(|| {
let mut map: HashMap<String, _> = HashMap::with_capacity(data.len() * 2);
for _ in 0..n {
for &i in &data {
match map.get_mut(i) {
Some(v) => { *v += 1; },
None => { map.insert(i.to_string(), 1); },
}
}
}
})
}
#[bench] fn get_or_insert_1(b: &mut Bencher) { get_or_insert(b, 1) }
#[bench] fn get_or_insert_2(b: &mut Bencher) { get_or_insert(b, 2) }
#[bench] fn get_or_insert_4(b: &mut Bencher) { get_or_insert(b, 4) }
#[bench] fn get_or_insert_8(b: &mut Bencher) { get_or_insert(b, 8) }
}