268 lines
7.2 KiB
Rust
268 lines
7.2 KiB
Rust
// 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()
|
|
}
|
|
}
|