use std::borrow::Borrow;
use std::fmt::{Debug, Formatter};
use std::hash::{BuildHasher, Hash, RandomState};
use std::pin::pin;
use std::sync::Arc;
use dashmap::DashMap;
use tokio::sync::Notify;
pub struct OnceMap<K, V, S = RandomState> {
items: DashMap<K, Value<V>, S>,
}
impl<K: Eq + Hash + Debug, V: Debug, S: BuildHasher + Clone> Debug for OnceMap<K, V, S> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.items, f)
}
}
impl<K: Eq + Hash, V: Clone, H: BuildHasher + Clone> OnceMap<K, V, H> {
pub fn with_hasher(hasher: H) -> Self {
Self {
items: DashMap::with_hasher(hasher),
}
}
pub fn with_capacity_and_hasher(capacity: usize, hasher: H) -> Self {
Self {
items: DashMap::with_capacity_and_hasher(capacity, hasher),
}
}
pub fn register(&self, key: K) -> bool {
let entry = self.items.entry(key);
match entry {
dashmap::mapref::entry::Entry::Occupied(_) => false,
dashmap::mapref::entry::Entry::Vacant(entry) => {
entry.insert(Value::Waiting(Arc::new(Notify::new())));
true
}
}
}
pub fn done(&self, key: K, value: V) {
if let Some(Value::Waiting(notify)) = self.items.insert(key, Value::Filled(value)) {
notify.notify_waiters();
}
}
pub async fn wait(&self, key: &K) -> Option<V> {
let notify = {
let entry = self.items.get(key)?;
match entry.value() {
Value::Filled(value) => return Some(value.clone()),
Value::Waiting(notify) => notify.clone(),
}
};
let notification = pin!(notify.notified());
if let Value::Filled(value) = self.items.get(key).expect("map is append-only").value() {
return Some(value.clone());
}
notification.await;
let entry = self.items.get(key).expect("map is append-only");
match entry.value() {
Value::Filled(value) => Some(value.clone()),
Value::Waiting(_) => unreachable!("notify was called"),
}
}
pub fn wait_blocking(&self, key: &K) -> Option<V> {
futures::executor::block_on(self.wait(key))
}
pub fn get<Q: ?Sized + Hash + Eq>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
{
let entry = self.items.get(key)?;
match entry.value() {
Value::Filled(value) => Some(value.clone()),
Value::Waiting(_) => None,
}
}
pub fn remove<Q: ?Sized + Hash + Eq>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
{
let entry = self.items.remove(key)?;
match entry {
(_, Value::Filled(value)) => Some(value),
(_, Value::Waiting(_)) => None,
}
}
}
impl<K: Eq + Hash + Clone, V, H: Default + BuildHasher + Clone> Default for OnceMap<K, V, H> {
fn default() -> Self {
Self {
items: DashMap::with_hasher(H::default()),
}
}
}
impl<K, V, H> FromIterator<(K, V)> for OnceMap<K, V, H>
where
K: Eq + Hash,
H: Default + Clone + BuildHasher,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
Self {
items: iter
.into_iter()
.map(|(k, v)| (k, Value::Filled(v)))
.collect(),
}
}
}
#[derive(Debug)]
enum Value<V> {
Waiting(Arc<Notify>),
Filled(V),
}