// 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 or the MIT license // , 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(usize, PhantomData<*const T>); impl Default for Interned { fn default() -> Self { INTERNER.intern_string(String::default()) } } impl Default for Interned { fn default() -> Self { INTERNER.intern_path(PathBuf::default()) } } 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s: &str = &*self; f.write_fmt(format_args!("{:?}", s)) } } impl fmt::Debug for Interned { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s: &Path = &*self; f.write_fmt(format_args!("{:?}", s)) } } impl Hash for Interned { fn hash(&self, state: &mut H) { let l = INTERNER.strs.lock().unwrap(); l.get(*self).hash(state) } } impl Hash for Interned { fn hash(&self, state: &mut H) { let l = INTERNER.paths.lock().unwrap(); l.get(*self).hash(state) } } impl Deref for Interned { 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 { 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 for Interned { fn as_ref(&self) -> &'static Path { let l = INTERNER.paths.lock().unwrap(); unsafe { mem::transmute::<&Path, &'static Path>(l.get(*self)) } } } impl AsRef for Interned { 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 for Interned { 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 for Interned { 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 { items: Vec, set: HashMap>, } impl TyIntern { fn new() -> TyIntern { TyIntern { items: Vec::new(), set: HashMap::new(), } } 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] } } pub struct Interner { strs: Mutex>, paths: Mutex>, } impl Interner { fn new() -> Interner { Interner { strs: Mutex::new(TyIntern::new()), paths: Mutex::new(TyIntern::new()), } } 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) } } 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, // 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 {:?} a second time", step); 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() } }