#![forbid(unsafe_code)]
use crate::sync::RwLock;
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash};
mod sync;
const DEFAULT_CAPACITY: usize = 1_024;
#[derive(Debug)]
pub struct Fett<K, V, F, H = RandomState> {
buckets: Vec<RwLock<Vec<(K, V)>>>,
hasher: H,
create: F,
}
impl<K, V, F> Fett<K, V, F>
where
K: Eq + Hash + PartialEq,
V: Clone,
F: Fn(&K) -> V,
{
pub fn new(create: F) -> Self {
Self::with_capacity(DEFAULT_CAPACITY, create)
}
pub fn with_capacity(capacity: usize, create: F) -> Fett<K, V, F, RandomState> {
Self::with_capacity_and_hasher(capacity, RandomState::new(), create)
}
}
impl<K, V, F, H> Fett<K, V, F, H>
where
K: Eq + Hash + PartialEq,
V: Clone,
F: Fn(&K) -> V,
H: BuildHasher,
{
pub fn with_hasher(hasher: H, create: F) -> Fett<K, V, F, H> {
Self::with_capacity_and_hasher(DEFAULT_CAPACITY, hasher, create)
}
pub fn with_capacity_and_hasher(capacity: usize, hasher: H, create: F) -> Fett<K, V, F, H> {
assert!(capacity >= 256);
let max_buckets = capacity / 16;
let max_pairs = 16;
let mut buckets = Vec::with_capacity(max_buckets);
for _ in 0..max_buckets {
let pairs = Vec::with_capacity(max_pairs);
buckets.push(RwLock::new(pairs));
}
Fett {
buckets,
hasher,
create,
}
}
}
impl<K, V, F, H> Fett<K, V, F, H>
where
K: Eq + Hash + PartialEq,
V: Clone,
F: Fn(&K) -> V,
H: BuildHasher,
{
pub fn get(&self, key: K) -> V {
let bucket = self.bucket(&key);
let guard = self.buckets[bucket].read();
if let Some(value) = guard
.iter()
.find_map(|(k, value)| (k == &key).then_some(value))
{
return value.clone();
}
drop(guard);
let mut write_guard = self.buckets[bucket].write();
if let Some(value) = write_guard
.iter()
.rev()
.find_map(|(k, value)| (k == &key).then_some(value))
{
return value.clone();
}
let value = (self.create)(&key);
write_guard.push((key, value.clone()));
value
}
pub fn remove(&self, key: &K) -> Option<V> {
let bucket = self.bucket(key);
let mut write_guard = self.buckets[bucket].write();
write_guard
.iter()
.position(|(k, _)| k == key)
.map(|index| write_guard.swap_remove(index).1)
}
pub fn contains(&self, key: &K) -> bool {
let bucket = self.bucket(key);
let guard = self.buckets[bucket].read();
guard.iter().any(|(k, _)| k == key)
}
pub fn into_inner(self) -> (H, F, Vec<(K, V)>) {
let kv = self
.buckets
.into_iter()
.flat_map(|lock| lock.into_inner())
.collect();
(self.hasher, self.create, kv)
}
fn bucket(&self, key: &K) -> usize {
self.hasher.hash_one(key) as usize % self.buckets.len()
}
}
impl<K, V, F, I> From<(F, I)> for Fett<K, V, F>
where
K: Eq + Hash + PartialEq,
V: Clone,
F: Fn(&K) -> V,
I: IntoIterator<Item = (K, V)>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
{
fn from((create, iter): (F, I)) -> Self {
let iter = iter.into_iter();
let fett = Self::with_capacity(iter.len().max(DEFAULT_CAPACITY), create);
for (key, value) in iter {
let bucket = fett.bucket(&key);
let mut write_guard = fett.buckets[bucket].write();
write_guard.push((key, value));
}
fett
}
}
impl<K, V, F, H, I> From<(H, F, I)> for Fett<K, V, F, H>
where
K: Eq + Hash + PartialEq,
V: Clone,
F: Fn(&K) -> V,
H: BuildHasher,
I: IntoIterator<Item = (K, V)>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
{
fn from((hasher, create, iter): (H, F, I)) -> Self {
let iter = iter.into_iter();
let fett = Self::with_capacity_and_hasher(iter.len().max(DEFAULT_CAPACITY), hasher, create);
for (key, value) in iter {
let bucket = fett.bucket(&key);
let mut write_guard = fett.buckets[bucket].write();
write_guard.push((key, value));
}
fett
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
use rayon::prelude::*;
use std::sync::atomic::{AtomicU8, Ordering};
use std::time::Duration;
const KEY1: &str = "7mohtcOFVz";
const KEY2: &str = "c1E51sSEyx";
#[test]
fn test_create_many() {
let counter = AtomicU8::new(0);
let fett = Fett::new(|_| {
std::thread::sleep(Duration::from_millis(100));
let count = counter.fetch_add(1, Ordering::Relaxed);
assert_eq!(count, 0);
count
});
[0_i32; 32].par_iter().for_each(|_| {
assert_eq!(fett.get(0), 0);
});
}
#[test]
fn test_remove() {
let hasher = fnv::FnvBuildHasher::default();
assert_eq!(hasher.hash_one(KEY1), hasher.hash_one(KEY2));
let fett = Fett::with_hasher(hasher.clone(), |_key| 0);
assert_eq!(fett.get(KEY1), 0);
assert!(fett.contains(&KEY1));
assert!(!fett.contains(&KEY2));
assert!(fett.remove(&KEY2).is_none());
assert_eq!(fett.get(KEY2), 0);
assert!(fett.contains(&KEY1));
assert!(fett.contains(&KEY2));
assert_eq!(fett.remove(&KEY2), Some(0));
assert!(fett.contains(&KEY1));
assert!(!fett.contains(&KEY2));
assert!(fett.remove(&KEY2).is_none());
let fett = Fett::with_hasher(hasher, |_key| 0);
assert_eq!(fett.get(KEY1), 0);
assert_eq!(fett.get(KEY2), 0);
assert_eq!(fett.remove(&KEY1), Some(0));
assert!(!fett.contains(&KEY1));
assert!(fett.contains(&KEY2));
}
#[test]
fn test_rehash() {
let hasher = fnv::FnvBuildHasher::default();
assert_eq!(hasher.hash_one(KEY1), hasher.hash_one(KEY2));
let fett = Fett::with_hasher(hasher, |key| format!("id {key}"));
fett.get(0);
fett.get(13);
fett.get(42);
assert_eq!(fett.buckets.len(), 64);
assert!(fett.contains(&0));
assert!(fett.contains(&13));
assert!(fett.contains(&42));
for i in 1000..3000 {
assert!(!fett.contains(&i));
}
assert!(!fett.contains(&3000));
let (hasher, create, kv) = fett.into_inner();
let fett = Fett::from((hasher, create, kv.into_iter()));
assert_eq!(fett.buckets.len(), 64);
assert!(fett.contains(&0));
assert!(fett.contains(&13));
assert!(fett.contains(&42));
for i in 1000..3000 {
assert!(!fett.contains(&i));
}
assert!(!fett.contains(&3000));
let (hasher, create, mut kv) = fett.into_inner();
kv.extend((1000..3000).map(|i| (i, create(&i))));
let fett = Fett::from((hasher, create, kv.into_iter()));
assert_eq!(fett.buckets.len(), 125);
assert!(fett.contains(&0));
assert!(fett.contains(&13));
assert!(fett.contains(&42));
for i in 1000..3000 {
assert!(fett.contains(&i));
}
assert!(!fett.contains(&3000));
}
}
#[cfg(all(test, loom))]
mod loom_tests;