diff --git a/src/cargo/core/source/source_id.rs b/src/cargo/core/source/source_id.rs index f30d18ffd05..47e8ef546f1 100644 --- a/src/cargo/core/source/source_id.rs +++ b/src/cargo/core/source/source_id.rs @@ -1,8 +1,10 @@ use std::cmp::{self, Ordering}; +use std::collections::HashSet; use std::fmt::{self, Formatter}; use std::hash::{self, Hash}; use std::path::Path; -use std::sync::Arc; +use std::ptr; +use std::sync::Mutex; use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT}; use std::sync::atomic::Ordering::SeqCst; @@ -16,13 +18,17 @@ use sources::{GitSource, PathSource, RegistrySource, CRATES_IO_INDEX}; use sources::DirectorySource; use util::{CargoResult, Config, ToUrl}; +lazy_static! { + static ref SOURCE_ID_CACHE: Mutex> = Mutex::new(HashSet::new()); +} + /// Unique identifier for a source of packages. -#[derive(Clone, Eq, Debug)] +#[derive(Clone, Copy, Eq, Debug)] pub struct SourceId { - inner: Arc, + inner: &'static SourceIdInner, } -#[derive(Eq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug, Hash)] struct SourceIdInner { /// The source URL url: Url, @@ -68,18 +74,28 @@ impl SourceId { /// /// The canonical url will be calculated, but the precise field will not fn new(kind: Kind, url: Url) -> CargoResult { - let source_id = SourceId { - inner: Arc::new(SourceIdInner { + let source_id = SourceId::wrap( + SourceIdInner { kind, canonical_url: git::canonicalize_url(&url)?, url, precise: None, name: None, - }), - }; + } + ); Ok(source_id) } + fn wrap(inner: SourceIdInner) -> SourceId { + let mut cache = SOURCE_ID_CACHE.lock().unwrap(); + let inner = cache.get(&inner).map(|&x| x).unwrap_or_else(|| { + let inner = Box::leak(Box::new(inner)); + cache.insert(inner); + inner + }); + SourceId { inner } + } + /// Parses a source URL and returns the corresponding ID. /// /// ## Example @@ -193,15 +209,15 @@ impl SourceId { pub fn alt_registry(config: &Config, key: &str) -> CargoResult { let url = config.get_registry_index(key)?; - Ok(SourceId { - inner: Arc::new(SourceIdInner { + Ok(SourceId::wrap( + SourceIdInner { kind: Kind::Registry, canonical_url: git::canonicalize_url(&url)?, url, precise: None, name: Some(key.to_string()), - }), - }) + } + )) } /// Get this source URL @@ -288,12 +304,12 @@ impl SourceId { /// Create a new SourceId from this source with the given `precise` pub fn with_precise(&self, v: Option) -> SourceId { - SourceId { - inner: Arc::new(SourceIdInner { + SourceId::wrap( + SourceIdInner { precise: v, ..(*self.inner).clone() - }), - } + } + ) } /// Whether the remote registry is the standard https://crates.io @@ -326,12 +342,6 @@ impl SourceId { } } -impl PartialEq for SourceId { - fn eq(&self, other: &SourceId) -> bool { - (*self.inner).eq(&*other.inner) - } -} - impl PartialOrd for SourceId { fn partial_cmp(&self, other: &SourceId) -> Option { Some(self.cmp(other)) @@ -425,24 +435,23 @@ impl fmt::Display for SourceId { } } -// This custom implementation handles situations such as when two git sources -// point at *almost* the same URL, but not quite, even when they actually point -// to the same repository. -/// This method tests for self and other values to be equal, and is used by ==. -/// -/// For git repositories, the canonical url is checked. -impl PartialEq for SourceIdInner { - fn eq(&self, other: &SourceIdInner) -> bool { - if self.kind != other.kind { +// Custom equality defined as canonical URL equality for git sources and +// URL equality for other sources, ignoring the `precise` and `name` fields. +impl PartialEq for SourceId { + fn eq(&self, other: &SourceId) -> bool { + if ptr::eq(self.inner, other.inner) { + return true; + } + if self.inner.kind != other.inner.kind { return false; } - if self.url == other.url { + if self.inner.url == other.inner.url { return true; } - match (&self.kind, &other.kind) { - (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => { - ref1 == ref2 && self.canonical_url == other.canonical_url + match (&self.inner.kind, &other.inner.kind) { + (Kind::Git(ref1), Kind::Git(ref2)) => { + ref1 == ref2 && self.inner.canonical_url == other.inner.canonical_url } _ => false, } diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index da01a776318..275fbe752a4 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -150,7 +150,7 @@ pub fn resolve_with_previous<'a, 'cfg>( // // TODO: This seems like a hokey reason to single out the registry as being // different - let mut to_avoid_sources = HashSet::new(); + let mut to_avoid_sources: HashSet<&SourceId> = HashSet::new(); if let Some(to_avoid) = to_avoid { to_avoid_sources.extend( to_avoid @@ -161,7 +161,7 @@ pub fn resolve_with_previous<'a, 'cfg>( } let keep = |p: &&'a PackageId| { - !to_avoid_sources.contains(&p.source_id()) && match to_avoid { + !to_avoid_sources.contains(p.source_id()) && match to_avoid { Some(set) => !set.contains(p), None => true, }