use std::any::{Any, TypeId}; use std::borrow::Borrow; use std::cell::RefCell; use std::cmp::Ordering; use std::collections::HashMap; use std::fmt; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::mem; use std::ops::Deref; use std::path::PathBuf; use std::sync::Mutex; // FIXME: replace with std::lazy after it gets stabilized and reaches beta use once_cell::sync::Lazy; use crate::builder::Step; pub struct Interned(usize, PhantomData<*const T>); impl Default for Interned { fn default() -> Self { T::default().intern() } } impl Copy for Interned {} impl Clone for Interned { fn clone(&self) -> Interned { *self } } impl PartialEq for Interned { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl Eq for Interned {} impl PartialEq for Interned { fn eq(&self, other: &str) -> bool { *self == other } } impl<'a> PartialEq<&'a str> for Interned { fn eq(&self, other: &&str) -> bool { **self == **other } } impl<'a, T> PartialEq<&'a Interned> for Interned { fn eq(&self, other: &&Self) -> bool { self.0 == other.0 } } impl<'a, T> PartialEq> for &'a Interned { fn eq(&self, other: &Interned) -> bool { self.0 == other.0 } } unsafe impl Send for Interned {} unsafe impl Sync for Interned {} impl fmt::Display for Interned { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s: &str = &*self; f.write_str(s) } } impl fmt::Debug for Interned where Self: Deref, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s: &U = &*self; f.write_fmt(format_args!("{s:?}")) } } impl Hash for Interned { fn hash(&self, state: &mut H) { let l = T::intern_cache().lock().unwrap(); l.get(*self).hash(state) } } impl Deref for Interned { type Target = T::Target; fn deref(&self) -> &Self::Target { let l = T::intern_cache().lock().unwrap(); unsafe { mem::transmute::<&Self::Target, &Self::Target>(l.get(*self)) } } } impl, U: ?Sized> AsRef for Interned { fn as_ref(&self) -> &U { let l = T::intern_cache().lock().unwrap(); unsafe { mem::transmute::<&U, &U>(l.get(*self).as_ref()) } } } impl PartialOrd for Interned { fn partial_cmp(&self, other: &Self) -> Option { let l = T::intern_cache().lock().unwrap(); l.get(*self).partial_cmp(l.get(*other)) } } impl Ord for Interned { fn cmp(&self, other: &Self) -> Ordering { let l = T::intern_cache().lock().unwrap(); l.get(*self).cmp(l.get(*other)) } } struct TyIntern { items: Vec, set: HashMap>, } impl Default for TyIntern { fn default() -> Self { TyIntern { items: Vec::new(), set: Default::default() } } } impl TyIntern { fn intern_borrow(&mut self, item: &B) -> Interned where B: Eq + Hash + ToOwned + ?Sized, T: Borrow, { if let Some(i) = self.set.get(&item) { return *i; } let item = item.to_owned(); let interned = Interned(self.items.len(), PhantomData::<*const T>); self.set.insert(item.clone(), interned); self.items.push(item); interned } fn intern(&mut self, item: T) -> Interned { if let Some(i) = self.set.get(&item) { return *i; } let interned = Interned(self.items.len(), PhantomData::<*const T>); self.set.insert(item.clone(), interned); self.items.push(item); interned } fn get(&self, i: Interned) -> &T { &self.items[i.0] } } #[derive(Default)] pub struct Interner { strs: Mutex>, paths: Mutex>, lists: Mutex>>, } trait Internable: Clone + Eq + Hash + 'static { fn intern_cache() -> &'static Mutex>; fn intern(self) -> Interned { Self::intern_cache().lock().unwrap().intern(self) } } impl Internable for String { fn intern_cache() -> &'static Mutex> { &INTERNER.strs } } impl Internable for PathBuf { fn intern_cache() -> &'static Mutex> { &INTERNER.paths } } impl Internable for Vec { fn intern_cache() -> &'static Mutex> { &INTERNER.lists } } impl Interner { pub fn intern_str(&self, s: &str) -> Interned { self.strs.lock().unwrap().intern_borrow(s) } pub fn intern_string(&self, s: String) -> Interned { self.strs.lock().unwrap().intern(s) } pub fn intern_path(&self, s: PathBuf) -> Interned { self.paths.lock().unwrap().intern(s) } pub fn intern_list(&self, v: Vec) -> Interned> { self.lists.lock().unwrap().intern(v) } } pub static INTERNER: Lazy = Lazy::new(Interner::default); /// This is essentially a `HashMap` which allows storing any type in its input and /// any type in its output. It is a write-once cache; values are never evicted, /// which means that references to the value can safely be returned from the /// `get()` method. #[derive(Debug)] pub struct Cache( RefCell< HashMap< TypeId, Box, // actually a HashMap> >, >, ); impl Cache { pub fn new() -> Cache { Cache(RefCell::new(HashMap::new())) } pub fn put(&self, step: S, value: S::Output) { let mut cache = self.0.borrow_mut(); let type_id = TypeId::of::(); let stepcache = cache .entry(type_id) .or_insert_with(|| Box::new(HashMap::::new())) .downcast_mut::>() .expect("invalid type mapped"); assert!(!stepcache.contains_key(&step), "processing {step:?} a second time"); stepcache.insert(step, value); } pub fn get(&self, step: &S) -> Option { let mut cache = self.0.borrow_mut(); let type_id = TypeId::of::(); let stepcache = cache .entry(type_id) .or_insert_with(|| Box::new(HashMap::::new())) .downcast_mut::>() .expect("invalid type mapped"); stepcache.get(step).cloned() } } #[cfg(test)] impl Cache { pub fn all(&mut self) -> Vec<(S, S::Output)> { let cache = self.0.get_mut(); let type_id = TypeId::of::(); let mut v = cache .remove(&type_id) .map(|b| b.downcast::>().expect("correct type")) .map(|m| m.into_iter().collect::>()) .unwrap_or_default(); v.sort_by_key(|(s, _)| s.clone()); v } pub fn contains(&self) -> bool { self.0.borrow().contains_key(&TypeId::of::()) } }