Skip to content

Commit 151c225

Browse files
committed
Auto merge of #6332 - dwijnand:intern-more, r=Eh2406
Intern PackageId Refs #6207
2 parents 6d57b59 + efd03bd commit 151c225

3 files changed

Lines changed: 113 additions & 40 deletions

File tree

src/cargo/core/package_id.rs

Lines changed: 90 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use std::cmp::Ordering;
1+
use std::collections::HashSet;
22
use std::fmt::{self, Formatter};
33
use std::hash;
44
use std::hash::Hash;
55
use std::path::Path;
6-
use std::sync::Arc;
6+
use std::ptr;
7+
use std::sync::Mutex;
78

89
use semver;
910
use serde::de;
@@ -13,19 +14,41 @@ use core::interning::InternedString;
1314
use core::source::SourceId;
1415
use util::{CargoResult, ToSemver};
1516

17+
lazy_static! {
18+
static ref PACKAGE_ID_CACHE: Mutex<HashSet<&'static PackageIdInner>> = Mutex::new(HashSet::new());
19+
}
20+
1621
/// Identifier for a specific version of a package in a specific source.
17-
#[derive(Clone)]
22+
#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
1823
pub struct PackageId {
19-
inner: Arc<PackageIdInner>,
24+
inner: &'static PackageIdInner,
2025
}
2126

22-
#[derive(PartialEq, PartialOrd, Eq, Ord)]
27+
#[derive(PartialOrd, Eq, Ord)]
2328
struct PackageIdInner {
2429
name: InternedString,
2530
version: semver::Version,
2631
source_id: SourceId,
2732
}
2833

34+
// Custom equality that uses full equality of SourceId, rather than its custom equality.
35+
impl PartialEq for PackageIdInner {
36+
fn eq(&self, other: &Self) -> bool {
37+
self.name == other.name
38+
&& self.version == other.version
39+
&& self.source_id.full_eq(&other.source_id)
40+
}
41+
}
42+
43+
// Custom hash that is coherent with the custom equality above.
44+
impl Hash for PackageIdInner {
45+
fn hash<S: hash::Hasher>(&self, into: &mut S) {
46+
self.name.hash(into);
47+
self.version.hash(into);
48+
self.source_id.full_hash(into);
49+
}
50+
}
51+
2952
impl ser::Serialize for PackageId {
3053
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
3154
where
@@ -64,51 +87,56 @@ impl<'de> de::Deserialize<'de> for PackageId {
6487
};
6588
let source_id = SourceId::from_url(url).map_err(de::Error::custom)?;
6689

67-
Ok(PackageId {
68-
inner: Arc::new(PackageIdInner {
90+
Ok(PackageId::wrap(
91+
PackageIdInner {
6992
name: InternedString::new(name),
7093
version,
7194
source_id,
72-
}),
73-
})
74-
}
75-
}
76-
77-
impl Hash for PackageId {
78-
fn hash<S: hash::Hasher>(&self, state: &mut S) {
79-
self.inner.name.hash(state);
80-
self.inner.version.hash(state);
81-
self.inner.source_id.hash(state);
95+
}
96+
))
8297
}
8398
}
8499

85100
impl PartialEq for PackageId {
86101
fn eq(&self, other: &PackageId) -> bool {
87-
(*self.inner).eq(&*other.inner)
88-
}
89-
}
90-
impl PartialOrd for PackageId {
91-
fn partial_cmp(&self, other: &PackageId) -> Option<Ordering> {
92-
(*self.inner).partial_cmp(&*other.inner)
102+
if ptr::eq(self.inner, other.inner) {
103+
return true;
104+
}
105+
self.inner.name == other.inner.name
106+
&& self.inner.version == other.inner.version
107+
&& self.inner.source_id == other.inner.source_id
93108
}
94109
}
95-
impl Eq for PackageId {}
96-
impl Ord for PackageId {
97-
fn cmp(&self, other: &PackageId) -> Ordering {
98-
(*self.inner).cmp(&*other.inner)
110+
111+
impl<'a> Hash for PackageId {
112+
fn hash<S: hash::Hasher>(&self, state: &mut S) {
113+
self.inner.name.hash(state);
114+
self.inner.version.hash(state);
115+
self.inner.source_id.hash(state);
99116
}
100117
}
101118

102119
impl PackageId {
103120
pub fn new<T: ToSemver>(name: &str, version: T, sid: SourceId) -> CargoResult<PackageId> {
104121
let v = version.to_semver()?;
105-
Ok(PackageId {
106-
inner: Arc::new(PackageIdInner {
122+
123+
Ok(PackageId::wrap(
124+
PackageIdInner {
107125
name: InternedString::new(name),
108126
version: v,
109127
source_id: sid,
110-
}),
111-
})
128+
}
129+
))
130+
}
131+
132+
fn wrap(inner: PackageIdInner) -> PackageId {
133+
let mut cache = PACKAGE_ID_CACHE.lock().unwrap();
134+
let inner = cache.get(&inner).map(|&x| x).unwrap_or_else(|| {
135+
let inner = Box::leak(Box::new(inner));
136+
cache.insert(inner);
137+
inner
138+
});
139+
PackageId { inner }
112140
}
113141

114142
pub fn name(&self) -> InternedString {
@@ -122,23 +150,23 @@ impl PackageId {
122150
}
123151

124152
pub fn with_precise(&self, precise: Option<String>) -> PackageId {
125-
PackageId {
126-
inner: Arc::new(PackageIdInner {
153+
PackageId::wrap(
154+
PackageIdInner {
127155
name: self.inner.name,
128156
version: self.inner.version.clone(),
129157
source_id: self.inner.source_id.with_precise(precise),
130-
}),
131-
}
158+
}
159+
)
132160
}
133161

134162
pub fn with_source_id(&self, source: SourceId) -> PackageId {
135-
PackageId {
136-
inner: Arc::new(PackageIdInner {
163+
PackageId::wrap(
164+
PackageIdInner {
137165
name: self.inner.name,
138166
version: self.inner.version.clone(),
139167
source_id: source,
140-
}),
141-
}
168+
}
169+
)
142170
}
143171

144172
pub fn stable_hash<'a>(&'a self, workspace: &'a Path) -> PackageIdStableHash<'a> {
@@ -195,4 +223,27 @@ mod tests {
195223
assert!(PackageId::new("foo", "bar", repo).is_err());
196224
assert!(PackageId::new("foo", "", repo).is_err());
197225
}
226+
227+
#[test]
228+
fn debug() {
229+
let loc = CRATES_IO_INDEX.to_url().unwrap();
230+
let pkg_id = PackageId::new("foo", "1.0.0", SourceId::for_registry(&loc).unwrap()).unwrap();
231+
assert_eq!(r#"PackageId { name: "foo", version: "1.0.0", source: "registry `https://github.com/rust-lang/crates.io-index`" }"#, format!("{:?}", pkg_id));
232+
233+
let pretty = r#"
234+
PackageId {
235+
name: "foo",
236+
version: "1.0.0",
237+
source: "registry `https://github.com/rust-lang/crates.io-index`"
238+
}
239+
"#.trim();
240+
assert_eq!(pretty, format!("{:#?}", pkg_id));
241+
}
242+
243+
#[test]
244+
fn display() {
245+
let loc = CRATES_IO_INDEX.to_url().unwrap();
246+
let pkg_id = PackageId::new("foo", "1.0.0", SourceId::for_registry(&loc).unwrap()).unwrap();
247+
assert_eq!("foo v1.0.0", pkg_id.to_string());
248+
}
198249
}

src/cargo/core/source/source_id.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ impl SourceId {
335335
}
336336
self.hash(into)
337337
}
338+
339+
pub fn full_eq(&self, other: &SourceId) -> bool {
340+
ptr::eq(self.inner, other.inner)
341+
}
342+
343+
pub fn full_hash<S: hash::Hasher>(&self, into: &mut S) {
344+
ptr::NonNull::from(self.inner).hash(into)
345+
}
338346
}
339347

340348
impl PartialOrd for SourceId {

tests/testsuite/git.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::thread;
1111
use support::paths::{self, CargoPathExt};
1212
use support::sleep_ms;
1313
use support::{basic_lib_manifest, basic_manifest, git, main_file, path2url, project};
14+
use support::Project;
1415

1516
#[test]
1617
fn cargo_compile_simple_git_dep() {
@@ -1090,6 +1091,18 @@ fn two_deps_only_update_one() {
10901091
).file("src/main.rs", "fn main() {}")
10911092
.build();
10921093

1094+
fn oid_to_short_sha(oid: git2::Oid) -> String {
1095+
oid.to_string()[..8].to_string()
1096+
}
1097+
fn git_repo_head_sha(p: &Project) -> String {
1098+
let repo = git2::Repository::open(p.root()).unwrap();
1099+
let head = repo.head().unwrap().target().unwrap();
1100+
oid_to_short_sha(head)
1101+
}
1102+
1103+
println!("dep1 head sha: {}", git_repo_head_sha(&git1));
1104+
println!("dep2 head sha: {}", git_repo_head_sha(&git2));
1105+
10931106
p.cargo("build")
10941107
.with_stderr(
10951108
"[UPDATING] git repository `[..]`\n\
@@ -1106,7 +1119,8 @@ fn two_deps_only_update_one() {
11061119
.unwrap();
11071120
let repo = git2::Repository::open(&git1.root()).unwrap();
11081121
git::add(&repo);
1109-
git::commit(&repo);
1122+
let oid = git::commit(&repo);
1123+
println!("dep1 head sha: {}", oid_to_short_sha(oid));
11101124

11111125
p.cargo("update -p dep1")
11121126
.with_stderr(&format!(

0 commit comments

Comments
 (0)