#![warn(missing_docs)]
pub mod iter;
mod loan;
#[cfg(test)]
mod tests;
pub use loan::Loan;
use std::{
collections::{hash_map::DefaultHasher, HashMap},
hash::{Hash, Hasher},
sync::atomic::{AtomicUsize, Ordering},
thread,
};
enum State<K, V> {
Present(K, V),
Loaned(K),
AwaitingDrop(K),
}
use self::State::{AwaitingDrop, Loaned, Present};
pub struct LendingLibrary<K, V>
where
K: Hash,
{
store: HashMap<u64, State<K, V>>,
outstanding: AtomicUsize,
}
fn _hash<K: Hash>(val: &K) -> u64 {
let mut hasher = DefaultHasher::new();
(*val).hash(&mut hasher);
hasher.finish()
}
impl<K, V> LendingLibrary<K, V>
where
K: Hash,
{
pub fn new() -> LendingLibrary<K, V> {
LendingLibrary {
store: HashMap::new(),
outstanding: AtomicUsize::new(0),
}
}
pub fn with_capacity(capacity: usize) -> LendingLibrary<K, V> {
LendingLibrary {
store: HashMap::with_capacity(capacity),
outstanding: AtomicUsize::new(0),
}
}
pub fn capacity(&self) -> usize {
self.store.capacity()
}
pub fn reserve(&mut self, additional: usize) {
self.store.reserve(additional)
}
pub fn shrink_to_fit(&mut self) {
self.store.shrink_to_fit()
}
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
self.into_iter()
}
pub fn len(&self) -> usize {
self.store
.iter()
.map(|(_k, v)| match v {
Present(..) | Loaned(_) => 1,
AwaitingDrop(_) => 0,
})
.sum()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn clear(&mut self) {
let new_store = self
.store
.drain()
.filter(|&(_k, ref v)| match v {
Present(..) => false,
Loaned(_) | AwaitingDrop(_) => true,
})
.map(|(h, v)| match v {
Loaned(k) | AwaitingDrop(k) => (h, AwaitingDrop(k)),
Present(..) => unreachable!(),
})
.collect();
self.store = new_store;
}
pub fn contains_key(&self, key: &K) -> bool {
let h = _hash(key);
match self.store.get(&h) {
Some(v) => match v {
Present(..) | Loaned(_) => true,
AwaitingDrop(_) => false,
},
None => false,
}
}
pub fn insert(&mut self, key: K, val: V) -> Option<V> {
let h = _hash(&key);
match self.store.insert(h, Present(key, val)) {
Some(v) => match v {
Present(_, v) => Some(v),
Loaned(_) => panic!("Cannot overwrite loaned value"),
AwaitingDrop(_) => panic!("Cannot overwrite value awaiting drop"),
},
None => None,
}
}
pub fn remove(&mut self, key: &K) -> bool {
let h = _hash(key);
match self.store.remove(&h) {
Some(v) => match v {
Present(..) => true,
Loaned(k) => {
self.store.insert(h, AwaitingDrop(k));
true
}
AwaitingDrop(k) => {
self.store.insert(h, AwaitingDrop(k));
false
}
},
None => false,
}
}
pub fn lend(&mut self, key: &K) -> Option<Loan<K, V>> {
let h = _hash(key);
let ptr: *mut Self = self;
match self.store.remove(&h) {
Some(v) => match v {
Present(k, v) => {
self.outstanding.fetch_add(1, Ordering::Relaxed);
self.store.insert(h, Loaned(k));
Some(Loan {
owner: ptr,
key: h,
inner: Some(v),
})
}
Loaned(_) => panic!("Lending already loaned value"),
AwaitingDrop(_) => panic!("Lending value awaiting drop"),
},
None => None,
}
}
fn checkin(&mut self, key: u64, val: V) {
match self.store.remove(&key) {
Some(v) => {
self.outstanding.fetch_sub(1, Ordering::Relaxed);
match v {
Present(..) => panic!("Returning replaced item"),
Loaned(k) => {
self.store.insert(key, Present(k, val));
}
AwaitingDrop(_) => {}
}
}
None => panic!("Returning item not from store"),
}
}
}
impl<K, V> Drop for LendingLibrary<K, V>
where
K: Hash,
{
fn drop(&mut self) {
if !thread::panicking() {
let count = self.outstanding.load(Ordering::SeqCst);
if count != 0 {
panic!("{} value loans outlived store.", count)
}
}
}
}
impl<K, V> Default for LendingLibrary<K, V>
where
K: Hash,
{
fn default() -> Self {
LendingLibrary::new()
}
}