use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash, Hasher};
const DEFAULT_MAX_DISPLACEMENTS: usize = 32;
const DEFAULT_MAX_LOAD_FACTOR: f64 = 0.49;
pub trait CuckooBuildHasher: BuildHasher {
fn seed(&self) -> u64;
}
#[derive(Clone)]
pub struct CuckooBuildHasherImpl {
seed: u64,
base: RandomState,
}
impl CuckooBuildHasher for CuckooBuildHasherImpl {
fn seed(&self) -> u64 {
self.seed
}
}
impl BuildHasher for CuckooBuildHasherImpl {
type Hasher = CuckooHasher;
fn build_hasher(&self) -> Self::Hasher {
CuckooHasher {
seed: self.seed,
base_hasher: self.base.build_hasher(),
}
}
}
#[derive(Debug)]
pub struct CuckooHasher {
seed: u64,
base_hasher: std::collections::hash_map::DefaultHasher, }
impl Hasher for CuckooHasher {
fn finish(&self) -> u64 {
let partial = self.base_hasher.finish();
partial ^ (self.seed.wrapping_mul(0x9E3779B185EBCA87))
}
fn write(&mut self, bytes: &[u8]) {
self.base_hasher.write(bytes);
}
}
#[derive(Debug)]
pub struct CuckooHashMap<K, V, BH1 = CuckooBuildHasherImpl, BH2 = CuckooBuildHasherImpl> {
capacity: usize,
len: usize,
table1: Vec<Option<(K, V)>>,
table2: Vec<Option<(K, V)>>,
buildhasher1: BH1,
buildhasher2: BH2,
max_displacements: usize,
max_load_factor: f64,
}
impl<K: Hash + Eq + Clone, V: Clone> Default for CuckooHashMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Hash + Eq + Clone, V: Clone> CuckooHashMap<K, V> {
pub fn new() -> Self {
Self::with_capacity(16)
}
}
impl<K: Hash + Eq + Clone, V: Clone>
CuckooHashMap<K, V, CuckooBuildHasherImpl, CuckooBuildHasherImpl>
{
pub fn with_capacity(cap: usize) -> Self {
use rand::{rngs::StdRng, Rng, SeedableRng};
let mut rng = StdRng::from_entropy();
let seed1 = rng.gen::<u64>();
let seed2 = rng.gen::<u64>();
Self::with_capacity_and_hasher(
cap,
CuckooBuildHasherImpl {
seed: seed1,
base: RandomState::new(),
},
CuckooBuildHasherImpl {
seed: seed2,
base: RandomState::new(),
},
)
}
}
impl<
K: Hash + Eq + Clone,
V: Clone,
BH1: CuckooBuildHasher + Clone,
BH2: CuckooBuildHasher + Clone,
> CuckooHashMap<K, V, BH1, BH2>
{
pub fn with_capacity_and_hasher(cap: usize, h1: BH1, h2: BH2) -> Self {
let capacity = cap.max(2).next_power_of_two();
let half = capacity / 2;
CuckooHashMap {
capacity,
len: 0,
table1: vec![None; half],
table2: vec![None; half],
buildhasher1: h1,
buildhasher2: h2,
max_displacements: DEFAULT_MAX_DISPLACEMENTS,
max_load_factor: DEFAULT_MAX_LOAD_FACTOR,
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
if let Some(vref) = self.get_mut(&key) {
let oldv = std::mem::replace(vref, value);
return Some(oldv);
}
if (self.len + 1) as f64 / (self.capacity as f64) > self.max_load_factor {
self.grow();
}
let r = self.cuckoo_place(key, value, self.max_displacements);
if r.is_err() {
self.grow();
let (k, v) = r.unwrap_err();
self.rehash();
self.cuckoo_place(k, v, self.max_displacements)
.unwrap_or_else(|_| panic!("Insertion failed even after rehash - unexpected"));
}
self.len += 1;
None
}
pub fn get(&self, key: &K) -> Option<&V> {
let (i1, i2) = self.indices_of(key);
if let Some((ref k, ref v)) = self.table1[i1] {
if k == key {
return Some(v);
}
}
if let Some((ref k, ref v)) = self.table2[i2] {
if k == key {
return Some(v);
}
}
None
}
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
let (i1, i2) = self.indices_of(key);
if let Some((ref k, ref mut v)) = self.table1[i1] {
if k == key {
return Some(v);
}
}
if let Some((ref k, ref mut v)) = self.table2[i2] {
if k == key {
return Some(v);
}
}
None
}
pub fn remove(&mut self, key: &K) -> Option<V> {
let (i1, i2) = self.indices_of(key);
if let Some((ref k, _)) = self.table1[i1] {
if k == key {
let (_, v) = self.table1[i1].take().unwrap();
self.len -= 1;
return Some(v);
}
}
if let Some((ref k, _)) = self.table2[i2] {
if k == key {
let (_, v) = self.table2[i2].take().unwrap();
self.len -= 1;
return Some(v);
}
}
None
}
pub fn clear(&mut self) {
self.table1.fill(None);
self.table2.fill(None);
self.len = 0;
}
fn indices_of(&self, key: &K) -> (usize, usize) {
let h1 = self.hash1(key);
let h2 = self.hash2(key);
let half = self.capacity / 2;
((h1 % half as u64) as usize, (h2 % half as u64) as usize)
}
fn hash1(&self, key: &K) -> u64 {
self.buildhasher1.hash_one(key)
}
fn hash2(&self, key: &K) -> u64 {
self.buildhasher2.hash_one(key)
}
fn cuckoo_place(&mut self, mut key: K, mut value: V, mut kicks: usize) -> Result<(), (K, V)> {
let half = self.capacity / 2;
let mut table_index = 1; let mut index = (self.hash1(&key) % (half as u64)) as usize;
loop {
let slot = match table_index {
1 => &mut self.table1[index],
2 => &mut self.table2[index],
_ => unreachable!(),
};
if slot.is_none() {
*slot = Some((key, value));
return Ok(());
} else {
let (old_key, old_val) = slot.take().unwrap();
*slot = Some((key, value));
key = old_key;
value = old_val;
table_index = if table_index == 1 { 2 } else { 1 };
index = if table_index == 1 {
(self.hash1(&key) % (half as u64)) as usize
} else {
(self.hash2(&key) % (half as u64)) as usize
};
kicks -= 1;
if kicks == 0 {
return Err((key, value));
}
}
}
}
fn grow(&mut self) {
let new_capacity = self.capacity * 2;
let half = new_capacity / 2;
let old_tab1 = std::mem::replace(&mut self.table1, vec![None; half]);
let old_tab2 = std::mem::replace(&mut self.table2, vec![None; half]);
self.capacity = new_capacity;
self.len = 0;
for (k, v) in old_tab1.into_iter().chain(old_tab2.into_iter()).flatten() {
self.insert(k, v);
}
}
fn rehash(&mut self) {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_insert_get_remove() {
let mut map = CuckooHashMap::with_capacity(8);
map.insert("hello", 123);
map.insert("world", 456);
assert_eq!(map.len(), 2);
assert_eq!(map.get(&"hello"), Some(&123));
assert_eq!(map.get(&"world"), Some(&456));
assert_eq!(map.get(&"foo"), None);
let old = map.insert("hello", 999);
assert_eq!(old, Some(123));
assert_eq!(map.get(&"hello"), Some(&999));
let rm = map.remove(&"world");
assert_eq!(rm, Some(456));
assert_eq!(map.get(&"world"), None);
assert_eq!(map.len(), 1);
}
#[test]
fn test_many_inserts() {
let mut map = CuckooHashMap::with_capacity(4);
for i in 0..50 {
map.insert(format!("key{}", i), i);
}
assert_eq!(map.len(), 50);
for i in 0..50 {
let key = format!("key{}", i);
assert_eq!(map.get(&key), Some(&i));
}
}
#[test]
fn test_collision_rehash() {
let mut map = CuckooHashMap::with_capacity(2);
let keys = vec!["a", "b", "c", "d", "e", "f", "g"];
for (i, k) in keys.iter().enumerate() {
map.insert(*k, i as i32);
}
for (i, k) in keys.iter().enumerate() {
assert_eq!(map.get(k), Some(&(i as i32)));
}
}
}