| // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use std::any::{Any, TypeId}; |
| use std::borrow::Borrow; |
| use std::cell::RefCell; |
| use std::collections::HashMap; |
| use std::convert::AsRef; |
| use std::ffi::OsStr; |
| use std::fmt; |
| use std::hash::{Hash, Hasher}; |
| use std::marker::PhantomData; |
| use std::mem; |
| use std::ops::Deref; |
| use std::path::{Path, PathBuf}; |
| use std::sync::Mutex; |
| |
| use builder::Step; |
| |
| pub struct Interned<T>(usize, PhantomData<*const T>); |
| |
| impl Default for Interned<String> { |
| fn default() -> Self { |
| INTERNER.intern_string(String::default()) |
| } |
| } |
| |
| impl Default for Interned<PathBuf> { |
| fn default() -> Self { |
| INTERNER.intern_path(PathBuf::default()) |
| } |
| } |
| |
| impl<T> Copy for Interned<T> {} |
| impl<T> Clone for Interned<T> { |
| fn clone(&self) -> Interned<T> { |
| *self |
| } |
| } |
| |
| impl<T> PartialEq for Interned<T> { |
| fn eq(&self, other: &Self) -> bool { |
| self.0 == other.0 |
| } |
| } |
| impl<T> Eq for Interned<T> {} |
| |
| impl PartialEq<str> for Interned<String> { |
| fn eq(&self, other: &str) -> bool { |
| *self == other |
| } |
| } |
| impl<'a> PartialEq<&'a str> for Interned<String> { |
| fn eq(&self, other: &&str) -> bool { |
| **self == **other |
| } |
| } |
| impl<'a, T> PartialEq<&'a Interned<T>> for Interned<T> { |
| fn eq(&self, other: &&Self) -> bool { |
| self.0 == other.0 |
| } |
| } |
| impl<'a, T> PartialEq<Interned<T>> for &'a Interned<T> { |
| fn eq(&self, other: &Interned<T>) -> bool { |
| self.0 == other.0 |
| } |
| } |
| |
| unsafe impl<T> Send for Interned<T> {} |
| unsafe impl<T> Sync for Interned<T> {} |
| |
| impl fmt::Display for Interned<String> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| let s: &str = &*self; |
| f.write_str(s) |
| } |
| } |
| |
| impl fmt::Debug for Interned<String> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| let s: &str = &*self; |
| f.write_fmt(format_args!("{:?}", s)) |
| } |
| } |
| impl fmt::Debug for Interned<PathBuf> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| let s: &Path = &*self; |
| f.write_fmt(format_args!("{:?}", s)) |
| } |
| } |
| |
| impl Hash for Interned<String> { |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| let l = INTERNER.strs.lock().unwrap(); |
| l.get(*self).hash(state) |
| } |
| } |
| |
| impl Hash for Interned<PathBuf> { |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| let l = INTERNER.paths.lock().unwrap(); |
| l.get(*self).hash(state) |
| } |
| } |
| |
| impl Deref for Interned<String> { |
| type Target = str; |
| fn deref(&self) -> &'static str { |
| let l = INTERNER.strs.lock().unwrap(); |
| unsafe { mem::transmute::<&str, &'static str>(l.get(*self)) } |
| } |
| } |
| |
| impl Deref for Interned<PathBuf> { |
| type Target = Path; |
| fn deref(&self) -> &'static Path { |
| let l = INTERNER.paths.lock().unwrap(); |
| unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) } |
| } |
| } |
| |
| impl AsRef<Path> for Interned<PathBuf> { |
| fn as_ref(&self) -> &'static Path { |
| let l = INTERNER.paths.lock().unwrap(); |
| unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) } |
| } |
| } |
| |
| impl AsRef<Path> for Interned<String> { |
| fn as_ref(&self) -> &'static Path { |
| let l = INTERNER.strs.lock().unwrap(); |
| unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self).as_ref()) } |
| } |
| } |
| |
| impl AsRef<OsStr> for Interned<PathBuf> { |
| fn as_ref(&self) -> &'static OsStr { |
| let l = INTERNER.paths.lock().unwrap(); |
| unsafe { mem::transmute::<&OsStr, &'static OsStr>(l.get(*self).as_ref()) } |
| } |
| } |
| |
| impl AsRef<OsStr> for Interned<String> { |
| fn as_ref(&self) -> &'static OsStr { |
| let l = INTERNER.strs.lock().unwrap(); |
| unsafe { mem::transmute::<&OsStr, &'static OsStr>(l.get(*self).as_ref()) } |
| } |
| } |
| |
| |
| struct TyIntern<T> { |
| items: Vec<T>, |
| set: HashMap<T, Interned<T>>, |
| } |
| |
| impl<T: Hash + Clone + Eq> TyIntern<T> { |
| fn new() -> TyIntern<T> { |
| TyIntern { |
| items: Vec::new(), |
| set: HashMap::new(), |
| } |
| } |
| |
| fn intern_borrow<B>(&mut self, item: &B) -> Interned<T> |
| where |
| B: Eq + Hash + ToOwned<Owned=T> + ?Sized, |
| T: Borrow<B>, |
| { |
| 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<T> { |
| 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>) -> &T { |
| &self.items[i.0] |
| } |
| } |
| |
| pub struct Interner { |
| strs: Mutex<TyIntern<String>>, |
| paths: Mutex<TyIntern<PathBuf>>, |
| } |
| |
| impl Interner { |
| fn new() -> Interner { |
| Interner { |
| strs: Mutex::new(TyIntern::new()), |
| paths: Mutex::new(TyIntern::new()), |
| } |
| } |
| |
| pub fn intern_str(&self, s: &str) -> Interned<String> { |
| self.strs.lock().unwrap().intern_borrow(s) |
| } |
| pub fn intern_string(&self, s: String) -> Interned<String> { |
| self.strs.lock().unwrap().intern(s) |
| } |
| |
| pub fn intern_path(&self, s: PathBuf) -> Interned<PathBuf> { |
| self.paths.lock().unwrap().intern(s) |
| } |
| } |
| |
| lazy_static! { |
| pub static ref INTERNER: Interner = Interner::new(); |
| } |
| |
| /// 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<Any>, // actually a HashMap<Step, Interned<Step::Output>> |
| >> |
| ); |
| |
| impl Cache { |
| pub fn new() -> Cache { |
| Cache(RefCell::new(HashMap::new())) |
| } |
| |
| pub fn put<S: Step>(&self, step: S, value: S::Output) { |
| let mut cache = self.0.borrow_mut(); |
| let type_id = TypeId::of::<S>(); |
| let stepcache = cache.entry(type_id) |
| .or_insert_with(|| Box::new(HashMap::<S, S::Output>::new())) |
| .downcast_mut::<HashMap<S, S::Output>>() |
| .expect("invalid type mapped"); |
| assert!(!stepcache.contains_key(&step), "processing {:?} a second time", step); |
| stepcache.insert(step, value); |
| } |
| |
| pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> { |
| let mut cache = self.0.borrow_mut(); |
| let type_id = TypeId::of::<S>(); |
| let stepcache = cache.entry(type_id) |
| .or_insert_with(|| Box::new(HashMap::<S, S::Output>::new())) |
| .downcast_mut::<HashMap<S, S::Output>>() |
| .expect("invalid type mapped"); |
| stepcache.get(step).cloned() |
| } |
| } |