Skip to content

Commit 5f83bb4

Browse files
committed
Auto merge of #5157 - Eh2406:more_interning, r=alexcrichton
More interning This is a forward approach to interning. Specifically `Dependency` and `PackageId` store their names as `InternedString`s and leave that value interned as long as possible. The alternative is to make a new `interned_name` function. The advantage of this approach is that a number of places in the code are doing `deb.name() == pid.name()` and are now using the fast pointer compare instead of the string compare, without the code needing to change. The disadvantage is that lots of places need to call `deref` with `&*` to convert to an `&str` and sum need to use `.to_inner()` to get a `&'static str`. In a test on #4810 (comment) Before we got to 10000000 ticks in ~48 sec After we got to 10000000 ticks in ~44 sec
2 parents 3f7a426 + 1fd3496 commit 5f83bb4

18 files changed

+72
-54
lines changed

src/cargo/core/dependency.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use semver::ReqParseError;
77
use serde::ser;
88

99
use core::{SourceId, Summary, PackageId};
10+
use core::interning::InternedString;
1011
use util::{Cfg, CfgExpr, Config};
1112
use util::errors::{CargoResult, CargoResultExt, CargoError};
1213

@@ -20,7 +21,7 @@ pub struct Dependency {
2021
/// The data underlying a Dependency.
2122
#[derive(PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Debug)]
2223
struct Inner {
23-
name: String,
24+
name: InternedString,
2425
source_id: SourceId,
2526
registry_id: Option<SourceId>,
2627
req: VersionReq,
@@ -63,7 +64,7 @@ impl ser::Serialize for Dependency {
6364
where S: ser::Serializer,
6465
{
6566
SerializedDependency {
66-
name: self.name(),
67+
name: &*self.name(),
6768
source: self.source_id(),
6869
req: self.version_req().to_string(),
6970
kind: self.kind(),
@@ -174,7 +175,7 @@ impl Dependency {
174175
pub fn new_override(name: &str, source_id: &SourceId) -> Dependency {
175176
Dependency {
176177
inner: Rc::new(Inner {
177-
name: name.to_string(),
178+
name: InternedString::new(name),
178179
source_id: source_id.clone(),
179180
registry_id: None,
180181
req: VersionReq::any(),
@@ -194,8 +195,8 @@ impl Dependency {
194195
&self.inner.req
195196
}
196197

197-
pub fn name(&self) -> &str {
198-
&self.inner.name
198+
pub fn name(&self) -> InternedString {
199+
self.inner.name
199200
}
200201

201202
pub fn source_id(&self) -> &SourceId {

src/cargo/core/interning.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::str;
66
use std::mem;
77
use std::cmp::Ordering;
88
use std::ops::Deref;
9+
use std::hash::{Hash, Hasher};
910

1011
pub fn leek(s: String) -> &'static str {
1112
let boxed = s.into_boxed_str();
@@ -23,7 +24,7 @@ lazy_static! {
2324
RwLock::new(HashSet::new());
2425
}
2526

26-
#[derive(Eq, PartialEq, Hash, Clone, Copy)]
27+
#[derive(Eq, PartialEq, Clone, Copy)]
2728
pub struct InternedString {
2829
ptr: *const u8,
2930
len: usize,
@@ -39,30 +40,43 @@ impl InternedString {
3940
cache.insert(s);
4041
InternedString { ptr: s.as_ptr(), len: s.len() }
4142
}
43+
pub fn to_inner(&self) -> &'static str {
44+
unsafe {
45+
let slice = slice::from_raw_parts(self.ptr, self.len);
46+
&str::from_utf8_unchecked(slice)
47+
}
48+
}
4249
}
4350

4451
impl Deref for InternedString {
4552
type Target = str;
4653

4754
fn deref(&self) -> &'static str {
48-
unsafe {
49-
let slice = slice::from_raw_parts(self.ptr, self.len);
50-
&str::from_utf8_unchecked(slice)
51-
}
55+
self.to_inner()
56+
}
57+
}
58+
59+
impl Hash for InternedString {
60+
fn hash<H: Hasher>(&self, state: &mut H) {
61+
self.to_inner().hash(state);
5262
}
5363
}
5464

5565
impl fmt::Debug for InternedString {
5666
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
57-
let str: &str = &*self;
58-
write!(f, "InternedString {{ {} }}", str)
67+
write!(f, "InternedString {{ {} }}", self.to_inner())
68+
}
69+
}
70+
71+
impl fmt::Display for InternedString {
72+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73+
write!(f, "{}", self.to_inner())
5974
}
6075
}
6176

6277
impl Ord for InternedString {
6378
fn cmp(&self, other: &InternedString) -> Ordering {
64-
let str: &str = &*self;
65-
str.cmp(&*other)
79+
self.to_inner().cmp(&*other)
6680
}
6781
}
6882

src/cargo/core/manifest.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use url::Url;
1010

1111
use core::{Dependency, PackageId, Summary, SourceId, PackageIdSpec};
1212
use core::{WorkspaceConfig, Epoch, Features, Feature};
13+
use core::interning::InternedString;
1314
use util::Config;
1415
use util::toml::TomlManifest;
1516
use util::errors::*;
@@ -301,7 +302,7 @@ impl Manifest {
301302
pub fn exclude(&self) -> &[String] { &self.exclude }
302303
pub fn include(&self) -> &[String] { &self.include }
303304
pub fn metadata(&self) -> &ManifestMetadata { &self.metadata }
304-
pub fn name(&self) -> &str { self.package_id().name() }
305+
pub fn name(&self) -> InternedString { self.package_id().name() }
305306
pub fn package_id(&self) -> &PackageId { self.summary.package_id() }
306307
pub fn summary(&self) -> &Summary { &self.summary }
307308
pub fn targets(&self) -> &[Target] { &self.targets }

src/cargo/core/package.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use lazycell::LazyCell;
1111

1212
use core::{Dependency, Manifest, PackageId, SourceId, Target};
1313
use core::{Summary, SourceMap};
14+
use core::interning::InternedString;
1415
use ops;
1516
use util::{Config, internal, lev_distance};
1617
use util::errors::{CargoResult, CargoResultExt};
@@ -55,7 +56,7 @@ impl ser::Serialize for Package {
5556
let description = manmeta.description.as_ref().map(String::as_ref);
5657

5758
SerializedPackage {
58-
name: package_id.name(),
59+
name: &*package_id.name(),
5960
version: &package_id.version().to_string(),
6061
id: package_id,
6162
license,
@@ -95,7 +96,7 @@ impl Package {
9596
/// Get the path to the manifest
9697
pub fn manifest_path(&self) -> &Path { &self.manifest_path }
9798
/// Get the name of the package
98-
pub fn name(&self) -> &str { self.package_id().name() }
99+
pub fn name(&self) -> InternedString { self.package_id().name() }
99100
/// Get the PackageId object for the package (fully defines a package)
100101
pub fn package_id(&self) -> &PackageId { self.manifest.package_id() }
101102
/// Get the root folder of the package

src/cargo/core/package_id.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use serde::ser;
1111

1212
use util::{CargoResult, ToSemver};
1313
use core::source::SourceId;
14+
use core::interning::InternedString;
1415

1516
/// Identifier for a specific version of a package in a specific source.
1617
#[derive(Clone)]
@@ -20,7 +21,7 @@ pub struct PackageId {
2021

2122
#[derive(PartialEq, PartialOrd, Eq, Ord)]
2223
struct PackageIdInner {
23-
name: String,
24+
name: InternedString,
2425
version: semver::Version,
2526
source_id: SourceId,
2627
}
@@ -63,7 +64,7 @@ impl<'de> de::Deserialize<'de> for PackageId {
6364

6465
Ok(PackageId {
6566
inner: Arc::new(PackageIdInner {
66-
name: name.to_string(),
67+
name: InternedString::new(name),
6768
version,
6869
source_id,
6970
}),
@@ -102,21 +103,21 @@ impl PackageId {
102103
let v = version.to_semver()?;
103104
Ok(PackageId {
104105
inner: Arc::new(PackageIdInner {
105-
name: name.to_string(),
106+
name: InternedString::new(name),
106107
version: v,
107108
source_id: sid.clone(),
108109
}),
109110
})
110111
}
111112

112-
pub fn name(&self) -> &str { &self.inner.name }
113+
pub fn name(&self) -> InternedString { self.inner.name }
113114
pub fn version(&self) -> &semver::Version { &self.inner.version }
114115
pub fn source_id(&self) -> &SourceId { &self.inner.source_id }
115116

116117
pub fn with_precise(&self, precise: Option<String>) -> PackageId {
117118
PackageId {
118119
inner: Arc::new(PackageIdInner {
119-
name: self.inner.name.to_string(),
120+
name: self.inner.name,
120121
version: self.inner.version.clone(),
121122
source_id: self.inner.source_id.with_precise(precise),
122123
}),
@@ -126,7 +127,7 @@ impl PackageId {
126127
pub fn with_source_id(&self, source: &SourceId) -> PackageId {
127128
PackageId {
128129
inner: Arc::new(PackageIdInner {
129-
name: self.inner.name.to_string(),
130+
name: self.inner.name,
130131
version: self.inner.version.clone(),
131132
source_id: source.clone(),
132133
}),

src/cargo/core/package_id_spec.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ impl PackageIdSpec {
115115
}
116116

117117
pub fn matches(&self, package_id: &PackageId) -> bool {
118-
if self.name() != package_id.name() { return false }
118+
if self.name() != &*package_id.name() { return false }
119119

120120
if let Some(ref v) = self.version {
121121
if v != package_id.version() {

src/cargo/core/registry.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ impl<'cfg> PackageRegistry<'cfg> {
317317
-> CargoResult<Option<Summary>> {
318318
for s in self.overrides.iter() {
319319
let src = self.sources.get_mut(s).unwrap();
320-
let dep = Dependency::new_override(dep.name(), s);
320+
let dep = Dependency::new_override(&*dep.name(), s);
321321
let mut results = src.query_vec(&dep)?;
322322
if !results.is_empty() {
323323
return Ok(Some(results.remove(0)))
@@ -519,7 +519,7 @@ fn lock(locked: &LockedMap,
519519
patches: &HashMap<Url, Vec<PackageId>>,
520520
summary: Summary) -> Summary {
521521
let pair = locked.get(summary.source_id()).and_then(|map| {
522-
map.get(summary.name())
522+
map.get(&*summary.name())
523523
}).and_then(|vec| {
524524
vec.iter().find(|&&(ref id, _)| id == summary.package_id())
525525
});
@@ -568,7 +568,7 @@ fn lock(locked: &LockedMap,
568568
// all known locked packages to see if they match this dependency.
569569
// If anything does then we lock it to that and move on.
570570
let v = locked.get(dep.source_id()).and_then(|map| {
571-
map.get(dep.name())
571+
map.get(&*dep.name())
572572
}).and_then(|vec| {
573573
vec.iter().find(|&&(ref id, _)| dep.matches_id(id))
574574
});
@@ -593,7 +593,7 @@ fn lock(locked: &LockedMap,
593593
assert!(remaining.next().is_none());
594594
let patch_source = patch_id.source_id();
595595
let patch_locked = locked.get(patch_source).and_then(|m| {
596-
m.get(patch_id.name())
596+
m.get(&*patch_id.name())
597597
}).map(|list| {
598598
list.iter().any(|&(ref id, _)| id == patch_id)
599599
}).unwrap_or(false);

src/cargo/core/resolver/mod.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ fn activation_error(cx: &Context,
10441044
for &(p, r) in links_errors.iter() {
10451045
if let ConflictReason::Links(ref link) = *r {
10461046
msg.push_str("\n\nthe package `");
1047-
msg.push_str(dep.name());
1047+
msg.push_str(&*dep.name());
10481048
msg.push_str("` links to the native library `");
10491049
msg.push_str(link);
10501050
msg.push_str("`, but it conflicts with a previous package which links to `");
@@ -1059,13 +1059,13 @@ fn activation_error(cx: &Context,
10591059
for &(p, r) in features_errors.iter() {
10601060
if let ConflictReason::MissingFeatures(ref features) = *r {
10611061
msg.push_str("\n\nthe package `");
1062-
msg.push_str(p.name());
1062+
msg.push_str(&*p.name());
10631063
msg.push_str("` depends on `");
1064-
msg.push_str(dep.name());
1064+
msg.push_str(&*dep.name());
10651065
msg.push_str("`, with features: `");
10661066
msg.push_str(features);
10671067
msg.push_str("` but `");
1068-
msg.push_str(dep.name());
1068+
msg.push_str(&*dep.name());
10691069
msg.push_str("` does not have these features.\n");
10701070
}
10711071
// p == parent so the full path is redundant.
@@ -1082,7 +1082,7 @@ fn activation_error(cx: &Context,
10821082
}
10831083

10841084
msg.push_str("\n\nfailed to select a version for `");
1085-
msg.push_str(dep.name());
1085+
msg.push_str(&*dep.name());
10861086
msg.push_str("` which could resolve this conflict");
10871087

10881088
return format_err!("{}", msg)
@@ -1274,7 +1274,7 @@ fn build_requirements<'a, 'b: 'a>(s: &'a Summary, method: &'b Method)
12741274
reqs.require_feature(key)?;
12751275
}
12761276
for dep in s.dependencies().iter().filter(|d| d.is_optional()) {
1277-
reqs.require_dependency(dep.name());
1277+
reqs.require_dependency(dep.name().to_inner());
12781278
}
12791279
}
12801280
Method::Required { features: requested_features, .. } => {
@@ -1304,7 +1304,7 @@ impl Context {
13041304
method: &Method) -> CargoResult<bool> {
13051305
let id = summary.package_id();
13061306
let prev = self.activations
1307-
.entry((InternedString::new(id.name()), id.source_id().clone()))
1307+
.entry((id.name(), id.source_id().clone()))
13081308
.or_insert_with(||Rc::new(Vec::new()));
13091309
if !prev.iter().any(|c| c == summary) {
13101310
self.resolve_graph.push(GraphNode::Add(id.clone()));
@@ -1365,13 +1365,13 @@ impl Context {
13651365
}
13661366

13671367
fn prev_active(&self, dep: &Dependency) -> &[Summary] {
1368-
self.activations.get(&(InternedString::new(dep.name()), dep.source_id().clone()))
1368+
self.activations.get(&(dep.name(), dep.source_id().clone()))
13691369
.map(|v| &v[..])
13701370
.unwrap_or(&[])
13711371
}
13721372

13731373
fn is_active(&self, id: &PackageId) -> bool {
1374-
self.activations.get(&(InternedString::new(id.name()), id.source_id().clone()))
1374+
self.activations.get(&(id.name(), id.source_id().clone()))
13751375
.map(|v| v.iter().any(|s| s.package_id() == id))
13761376
.unwrap_or(false)
13771377
}
@@ -1397,12 +1397,12 @@ impl Context {
13971397
// Next, collect all actually enabled dependencies and their features.
13981398
for dep in deps {
13991399
// Skip optional dependencies, but not those enabled through a feature
1400-
if dep.is_optional() && !reqs.deps.contains_key(dep.name()) {
1400+
if dep.is_optional() && !reqs.deps.contains_key(&*dep.name()) {
14011401
continue
14021402
}
14031403
// So we want this dependency. Move the features we want from `feature_deps`
14041404
// to `ret`.
1405-
let base = reqs.deps.remove(dep.name()).unwrap_or((false, vec![]));
1405+
let base = reqs.deps.remove(&*dep.name()).unwrap_or((false, vec![]));
14061406
if !dep.is_optional() && base.0 {
14071407
self.warnings.push(
14081408
format!("Package `{}` does not have feature `{}`. It has a required dependency \

src/cargo/core/summary.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl Summary {
3232
features: BTreeMap<String, Vec<String>>,
3333
links: Option<String>) -> CargoResult<Summary> {
3434
for dep in dependencies.iter() {
35-
if features.get(dep.name()).is_some() {
35+
if features.get(&*dep.name()).is_some() {
3636
bail!("Features and dependencies cannot have the \
3737
same name: `{}`", dep.name())
3838
}
@@ -47,7 +47,7 @@ impl Summary {
4747
let dep = parts.next().unwrap();
4848
let is_reexport = parts.next().is_some();
4949
if !is_reexport && features.get(dep).is_some() { continue }
50-
match dependencies.iter().find(|d| d.name() == dep) {
50+
match dependencies.iter().find(|d| &*d.name() == dep) {
5151
Some(d) => {
5252
if d.is_optional() || is_reexport { continue }
5353
bail!("Feature `{}` depends on `{}` which is not an \
@@ -78,7 +78,7 @@ impl Summary {
7878
}
7979

8080
pub fn package_id(&self) -> &PackageId { &self.inner.package_id }
81-
pub fn name(&self) -> &str { self.package_id().name() }
81+
pub fn name(&self) -> InternedString { self.package_id().name() }
8282
pub fn version(&self) -> &Version { self.package_id().version() }
8383
pub fn source_id(&self) -> &SourceId { self.package_id().source_id() }
8484
pub fn dependencies(&self) -> &[Dependency] { &self.inner.dependencies }

src/cargo/ops/cargo_doc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub fn doc(ws: &Workspace, options: &DocOptions) -> CargoResult<()> {
5454
bail!("Passing multiple packages and `open` is not supported.\n\
5555
Please re-run this command with `-p <spec>` where `<spec>` \
5656
is one of the following:\n {}",
57-
pkgs.iter().map(|p| p.name()).collect::<Vec<_>>().join("\n "));
57+
pkgs.iter().map(|p| p.name().to_inner()).collect::<Vec<_>>().join("\n "));
5858
} else if pkgs.len() == 1 {
5959
pkgs[0].name().replace("-", "_")
6060
} else {

src/cargo/ops/cargo_generate_lockfile.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub fn update_lockfile(ws: &Workspace, opts: &UpdateOptions)
136136
resolve: &'a Resolve) ->
137137
Vec<(Vec<&'a PackageId>, Vec<&'a PackageId>)> {
138138
fn key(dep: &PackageId) -> (&str, &SourceId) {
139-
(dep.name(), dep.source_id())
139+
(dep.name().to_inner(), dep.source_id())
140140
}
141141

142142
// Removes all package ids in `b` from `a`. Note that this is somewhat

0 commit comments

Comments
 (0)