use dyn_cache::sync::SendCache;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::{
any::{type_name, TypeId},
borrow::Borrow,
collections::HashMap,
fmt::{Debug, Formatter, Result as FmtResult},
hash::{Hash, Hasher},
marker::PhantomData,
};
static TOKENS: Lazy<Mutex<SendCache>> = Lazy::new(|| Mutex::new(SendCache::default()));
pub(crate) struct Slot<T> {
index: u32,
ty: PhantomData<T>,
}
impl<T> Slot<T>
where
T: Eq + Hash + Send + 'static,
{
pub fn make<Q>(value: &Q) -> Slot<T>
where
Q: Eq + Hash + ToOwned<Owned = T> + ?Sized,
T: Borrow<Q>,
{
static INDICES: Lazy<Mutex<HashMap<TypeId, u32>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
let mut existing_tokens = TOKENS.lock();
match existing_tokens.get(value, &()) {
Ok(token) => *token,
Err(miss) => {
let (to_store, new_token) = miss.init(|_| {
let mut indices = INDICES.lock();
let count = indices.entry(TypeId::of::<T>()).or_default();
*count += 1;
let new_token = Self { index: *count, ty: PhantomData };
(new_token, new_token)
});
existing_tokens.store(to_store);
new_token
}
}
}
pub(crate) fn fake() -> Self {
Self { index: 0, ty: PhantomData }
}
}
impl<T> Clone for Slot<T> {
fn clone(&self) -> Self {
Self { index: self.index, ty: PhantomData }
}
}
impl<T> Copy for Slot<T> {}
impl<T> Debug for Slot<T> {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.debug_struct("Token").field("index", &self.index).field("ty", &type_name::<T>()).finish()
}
}
impl<Q, T> From<&Q> for Slot<T>
where
Q: Eq + Hash + ToOwned<Owned = T> + ?Sized,
T: Borrow<Q> + Eq + Hash + Send + 'static,
{
fn from(query: &Q) -> Self {
Slot::make(query)
}
}
impl<T> Hash for Slot<T> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.index.hash(hasher)
}
}
impl<T> PartialEq for Slot<T> {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
impl<T> Eq for Slot<T> {}
impl<T> PartialOrd for Slot<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.index.partial_cmp(&other.index)
}
}
impl<T> Ord for Slot<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.index.cmp(&other.index)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) struct OpaqueSlot {
ty: TypeId,
index: u32,
}
impl<T: 'static> From<Slot<T>> for OpaqueSlot {
fn from(token: Slot<T>) -> Self {
OpaqueSlot { index: token.index, ty: TypeId::of::<T>() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_tokens() {
let foo: Slot<String> = Slot::make("foo");
assert_eq!(foo, Slot::make("foo"));
assert_ne!(foo, Slot::make("bar"));
}
#[test]
fn make_opaque() {
let first: OpaqueSlot = Slot::make(&10u8).into();
let second: OpaqueSlot = Slot::make(&10u16).into();
assert_ne!(first, second);
}
}