use std::borrow::Borrow;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::hash::RandomState;
use std::sync::Arc;
use crate::internal::Mutex;
use crate::internal::OnceTable;
use crate::internal::OnceTableEntry;
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct OnceMap<K, V, S = RandomState> {
map: Mutex<OnceTable<K, V, S>>,
}
struct ComputeCleanupGuard<'a, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
once_map: &'a OnceMap<K, V, S>,
entry: Option<Arc<OnceTableEntry<K, V>>>,
}
impl<'a, K, V, S> ComputeCleanupGuard<'a, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
fn new(once_map: &'a OnceMap<K, V, S>, entry: Arc<OnceTableEntry<K, V>>) -> Self {
Self {
once_map,
entry: Some(entry),
}
}
fn entry(&self) -> &Arc<OnceTableEntry<K, V>> {
self.entry.as_ref().unwrap()
}
fn dismiss(mut self) {
drop(self.entry.take());
}
}
impl<K, V, S> Drop for ComputeCleanupGuard<'_, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
fn drop(&mut self) {
let Some(entry) = self.entry.take() else {
return;
};
let mut table = self.once_map.map.lock();
if Arc::strong_count(&entry) == 2 && !entry.initialized() {
table.remove_entry(&entry);
}
drop(entry);
}
}
impl<K, V, S> Default for OnceMap<K, V, S>
where
K: Eq + Hash,
V: Clone,
S: BuildHasher + Default,
{
fn default() -> Self {
Self::with_hasher(S::default())
}
}
impl<K, V> OnceMap<K, V, RandomState>
where
K: Eq + Hash,
V: Clone,
{
pub fn new() -> Self {
Self {
map: Mutex::new(OnceTable::with_hasher(RandomState::new())),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
map: Mutex::new(OnceTable::with_capacity_and_hasher(
capacity,
RandomState::new(),
)),
}
}
}
impl<K, V, S> OnceMap<K, V, S>
where
K: Eq + Hash,
V: Clone,
S: BuildHasher,
{
pub fn with_hasher(hasher: S) -> Self {
Self {
map: Mutex::new(OnceTable::with_hasher(hasher)),
}
}
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self {
Self {
map: Mutex::new(OnceTable::with_capacity_and_hasher(capacity, hasher)),
}
}
pub async fn compute<F>(&self, key: K, func: F) -> V
where
F: AsyncFnOnce() -> V,
{
let entry = {
let mut map = self.map.lock();
let entry = map.get_or_insert(key);
if let Some(value) = entry.get() {
return value.clone();
}
Arc::clone(entry)
};
let guard = ComputeCleanupGuard::new(self, entry);
let result = guard.entry().get_or_init(func).await.clone();
guard.dismiss();
result
}
pub async fn try_compute<E, F>(&self, key: K, func: F) -> Result<V, E>
where
F: AsyncFnOnce() -> Result<V, E>,
{
let entry = {
let mut map = self.map.lock();
let entry = map.get_or_insert(key);
if let Some(value) = entry.get() {
return Ok(value.clone());
}
Arc::clone(entry)
};
let guard = ComputeCleanupGuard::new(self, entry);
let result = guard.entry().get_or_try_init(func).await?.clone();
guard.dismiss();
Ok(result)
}
pub fn get<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let map = self.map.lock();
let entry = map.get(key)?;
entry.get().cloned()
}
pub fn discard<Q>(&self, key: &Q)
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let mut map = self.map.lock();
map.remove(key);
}
pub fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let entry = self.map.lock().remove(key)?;
entry.get().cloned()
}
}
impl<K, V, S> FromIterator<(K, V)> for OnceMap<K, V, S>
where
K: Eq + Hash,
V: Clone,
S: Default + BuildHasher,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let mut map = OnceTable::with_hasher(S::default());
for (key, value) in iter {
map.insert(key, value);
}
Self {
map: Mutex::new(map),
}
}
}