Skip to content

Commit d9f270e

Browse files
committed
Replace String with Symbol in Cache.paths et al.
This should hopefully improve memory usage and rustdoc run time. It's also more consistent with the rest of rustc and rustdoc.
1 parent 4bac09f commit d9f270e

File tree

12 files changed

+88
-49
lines changed

12 files changed

+88
-49
lines changed

src/librustdoc/clean/inline.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,11 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
162162
/// These names are used later on by HTML rendering to generate things like
163163
/// source links back to the original item.
164164
crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: ItemType) {
165-
let crate_name = cx.tcx.crate_name(did.krate).to_string();
165+
let crate_name = cx.tcx.crate_name(did.krate);
166166

167-
let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
168-
// extern blocks have an empty name
169-
let s = elem.data.to_string();
170-
if !s.is_empty() { Some(s) } else { None }
171-
});
167+
// FIXME: use def_id_to_path instead
168+
let relative =
169+
cx.tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name());
172170
let fqn = if let ItemType::Macro = kind {
173171
// Check to see if it is a macro 2.0 or built-in macro
174172
if matches!(

src/librustdoc/clean/utils.rs

+17
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,20 @@ pub(super) fn display_macro_source(
536536
}
537537
}
538538
}
539+
540+
crate fn join_path_segments(segments: &[Symbol]) -> String {
541+
join_path_segments_with_sep(segments, "::")
542+
}
543+
544+
crate fn join_path_segments_with_sep(segments: &[Symbol], sep: &str) -> String {
545+
let mut out = String::new();
546+
// This can't use an iterator intersperse because then it would be borrowing from
547+
// temporary SymbolStrs due to the lifetimes on SymbolStr's Deref impl.
548+
for (i, s) in segments.iter().enumerate() {
549+
if i > 0 {
550+
out.push_str(sep);
551+
}
552+
out.push_str(&s.as_str())
553+
}
554+
out
555+
}

src/librustdoc/formats/cache.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX};
55
use rustc_middle::middle::privacy::AccessLevels;
66
use rustc_middle::ty::TyCtxt;
77
use rustc_span::symbol::sym;
8+
use rustc_span::Symbol;
89

10+
use crate::clean::utils::join_path_segments;
911
use crate::clean::{self, ExternalCrate, ItemId, PrimitiveType};
1012
use crate::core::DocContext;
1113
use crate::fold::DocFolder;
@@ -39,11 +41,11 @@ crate struct Cache {
3941
/// URLs when a type is being linked to. External paths are not located in
4042
/// this map because the `External` type itself has all the information
4143
/// necessary.
42-
crate paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
44+
crate paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
4345

4446
/// Similar to `paths`, but only holds external paths. This is only used for
4547
/// generating explicit hyperlinks to other crates.
46-
crate external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
48+
crate external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
4749

4850
/// Maps local `DefId`s of exported types to fully qualified paths.
4951
/// Unlike 'paths', this mapping ignores any renames that occur
@@ -55,7 +57,7 @@ crate struct Cache {
5557
/// to the path used if the corresponding type is inlined. By
5658
/// doing this, we can detect duplicate impls on a trait page, and only display
5759
/// the impl for the inlined type.
58-
crate exact_paths: FxHashMap<DefId, Vec<String>>,
60+
crate exact_paths: FxHashMap<DefId, Vec<Symbol>>,
5961

6062
/// This map contains information about all known traits of this crate.
6163
/// Implementations of a crate should inherit the documentation of the
@@ -92,7 +94,7 @@ crate struct Cache {
9294
crate masked_crates: FxHashSet<CrateNum>,
9395

9496
// Private fields only used when initially crawling a crate to build a cache
95-
stack: Vec<String>,
97+
stack: Vec<Symbol>,
9698
parent_stack: Vec<DefId>,
9799
parent_is_trait_impl: bool,
98100
stripped_mod: bool,
@@ -156,7 +158,7 @@ impl Cache {
156158
let dst = &render_options.output;
157159
let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx);
158160
cx.cache.extern_locations.insert(e.crate_num, location);
159-
cx.cache.external_paths.insert(e.def_id(), (vec![name.to_string()], ItemType::Module));
161+
cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module));
160162
}
161163

162164
// FIXME: avoid this clone (requires implementing Default manually)
@@ -165,10 +167,9 @@ impl Cache {
165167
let crate_name = tcx.crate_name(def_id.krate);
166168
// Recall that we only allow primitive modules to be at the root-level of the crate.
167169
// If that restriction is ever lifted, this will have to include the relative paths instead.
168-
cx.cache.external_paths.insert(
169-
def_id,
170-
(vec![crate_name.to_string(), prim.as_sym().to_string()], ItemType::Primitive),
171-
);
170+
cx.cache
171+
.external_paths
172+
.insert(def_id, (vec![crate_name, prim.as_sym()], ItemType::Primitive));
172173
}
173174

174175
krate = CacheBuilder { tcx, cache: &mut cx.cache }.fold_crate(krate);
@@ -300,7 +301,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
300301
self.cache.search_index.push(IndexItem {
301302
ty: item.type_(),
302303
name: s.to_string(),
303-
path: path.join("::"),
304+
path: join_path_segments(path),
304305
desc,
305306
parent,
306307
parent_idx: None,
@@ -321,7 +322,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
321322
// Keep track of the fully qualified path for this item.
322323
let pushed = match item.name {
323324
Some(n) if !n.is_empty() => {
324-
self.cache.stack.push(n.to_string());
325+
self.cache.stack.push(n);
325326
true
326327
}
327328
_ => false,

src/librustdoc/html/format.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ use rustc_middle::ty;
1919
use rustc_middle::ty::DefIdTree;
2020
use rustc_middle::ty::TyCtxt;
2121
use rustc_span::def_id::CRATE_DEF_INDEX;
22+
use rustc_span::Symbol;
2223
use rustc_target::spec::abi::Abi;
2324

25+
use crate::clean::utils::join_path_segments;
2426
use crate::clean::{self, utils::find_nearest_parent_module, ExternalCrate, ItemId, PrimitiveType};
2527
use crate::formats::item_type::ItemType;
2628
use crate::html::escape::Escape;
@@ -505,7 +507,7 @@ crate fn href_with_root_path(
505507
did: DefId,
506508
cx: &Context<'_>,
507509
root_path: Option<&str>,
508-
) -> Result<(String, ItemType, Vec<String>), HrefError> {
510+
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
509511
let tcx = cx.tcx();
510512
let def_kind = tcx.def_kind(did);
511513
let did = match def_kind {
@@ -517,7 +519,7 @@ crate fn href_with_root_path(
517519
};
518520
let cache = cx.cache();
519521
let relative_to = &cx.current;
520-
fn to_module_fqp(shortty: ItemType, fqp: &[String]) -> &[String] {
522+
fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] {
521523
if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] }
522524
}
523525

@@ -547,7 +549,7 @@ crate fn href_with_root_path(
547549
is_remote = true;
548550
let s = s.trim_end_matches('/');
549551
let mut builder = UrlPartsBuilder::singleton(s);
550-
builder.extend(module_fqp.iter().map(String::as_str));
552+
builder.extend(module_fqp.iter().copied());
551553
builder
552554
}
553555
ExternalLocation::Local => href_relative_parts(module_fqp, relative_to),
@@ -566,7 +568,7 @@ crate fn href_with_root_path(
566568
}
567569
}
568570
debug!(?url_parts);
569-
let last = &fqp.last().unwrap()[..];
571+
let last = &*fqp.last().unwrap().as_str();
570572
match shortty {
571573
ItemType::Module => {
572574
url_parts.push("index.html");
@@ -579,25 +581,28 @@ crate fn href_with_root_path(
579581
Ok((url_parts.finish(), shortty, fqp.to_vec()))
580582
}
581583

582-
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<String>), HrefError> {
584+
crate fn href(did: DefId, cx: &Context<'_>) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
583585
href_with_root_path(did, cx, None)
584586
}
585587

586588
/// Both paths should only be modules.
587589
/// This is because modules get their own directories; that is, `std::vec` and `std::vec::Vec` will
588590
/// both need `../iter/trait.Iterator.html` to get at the iterator trait.
589-
crate fn href_relative_parts(fqp: &[String], relative_to_fqp: &[String]) -> UrlPartsBuilder {
591+
crate fn href_relative_parts(fqp: &[Symbol], relative_to_fqp: &[String]) -> UrlPartsBuilder {
590592
for (i, (f, r)) in fqp.iter().zip(relative_to_fqp.iter()).enumerate() {
591593
// e.g. linking to std::iter from std::vec (`dissimilar_part_count` will be 1)
592-
if f != r {
594+
if &*f.as_str() != r {
593595
let dissimilar_part_count = relative_to_fqp.len() - i;
594-
let fqp_module = fqp[i..fqp.len()].iter().map(String::as_str);
595-
return iter::repeat("..").take(dissimilar_part_count).chain(fqp_module).collect();
596+
let fqp_module = &fqp[i..fqp.len()];
597+
let mut builder: UrlPartsBuilder =
598+
iter::repeat("..").take(dissimilar_part_count).collect();
599+
builder.extend(fqp_module.iter().copied());
600+
return builder;
596601
}
597602
}
598603
// e.g. linking to std::sync::atomic from std::sync
599604
if relative_to_fqp.len() < fqp.len() {
600-
fqp[relative_to_fqp.len()..fqp.len()].iter().map(String::as_str).collect()
605+
fqp[relative_to_fqp.len()..fqp.len()].iter().copied().collect()
601606
// e.g. linking to std::sync from std::sync::atomic
602607
} else if fqp.len() < relative_to_fqp.len() {
603608
let dissimilar_part_count = relative_to_fqp.len() - fqp.len();
@@ -631,8 +636,8 @@ fn resolved_path<'cx>(
631636
if let Ok((_, _, fqp)) = href(did, cx) {
632637
format!(
633638
"{}::{}",
634-
fqp[..fqp.len() - 1].join("::"),
635-
anchor(did, fqp.last().unwrap(), cx)
639+
join_path_segments(&fqp[..fqp.len() - 1]),
640+
anchor(did, &fqp.last().unwrap().as_str(), cx)
636641
)
637642
} else {
638643
last.name.to_string()
@@ -743,7 +748,7 @@ crate fn anchor<'a, 'cx: 'a>(
743748
short_ty,
744749
url,
745750
short_ty,
746-
fqp.join("::"),
751+
join_path_segments(&fqp),
747752
text
748753
)
749754
} else {
@@ -961,7 +966,7 @@ fn fmt_type<'cx>(
961966
url = url,
962967
shortty = ItemType::AssocType,
963968
name = name,
964-
path = path.join("::")
969+
path = join_path_segments(path),
965970
)?;
966971
}
967972
_ => write!(f, "{}", name)?,

src/librustdoc/html/render/cache.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer};
88

99
use crate::clean;
1010
use crate::clean::types::{FnDecl, FnRetTy, GenericBound, Generics, Type, WherePredicate};
11+
use crate::clean::utils::join_path_segments;
1112
use crate::formats::cache::Cache;
1213
use crate::formats::item_type::ItemType;
1314
use crate::html::markdown::short_markdown_summary;
@@ -38,7 +39,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
3839
cache.search_index.push(IndexItem {
3940
ty: item.type_(),
4041
name: item.name.unwrap().to_string(),
41-
path: fqp[..fqp.len() - 1].join("::"),
42+
path: join_path_segments(&fqp[..fqp.len() - 1]),
4243
desc,
4344
parent: Some(did),
4445
parent_idx: None,
@@ -90,7 +91,7 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
9091
lastpathid += 1;
9192

9293
if let Some(&(ref fqp, short)) = paths.get(&defid) {
93-
crate_paths.push((short, fqp.last().unwrap().clone()));
94+
crate_paths.push((short, fqp.last().unwrap().to_string()));
9495
Some(pathid)
9596
} else {
9697
None

src/librustdoc/html/render/context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -237,18 +237,18 @@ impl<'tcx> Context<'tcx> {
237237
if let Some(&(ref names, ty)) = self.cache().paths.get(&it.def_id.expect_def_id()) {
238238
let mut path = String::new();
239239
for name in &names[..names.len() - 1] {
240-
path.push_str(name);
240+
path.push_str(&name.as_str());
241241
path.push('/');
242242
}
243-
path.push_str(&item_path(ty, names.last().unwrap()));
243+
path.push_str(&item_path(ty, &names.last().unwrap().as_str()));
244244
match self.shared.redirections {
245245
Some(ref redirections) => {
246246
let mut current_path = String::new();
247247
for name in &self.current {
248248
current_path.push_str(name);
249249
current_path.push('/');
250250
}
251-
current_path.push_str(&item_path(ty, names.last().unwrap()));
251+
current_path.push_str(&item_path(ty, &names.last().unwrap().as_str()));
252252
redirections.borrow_mut().insert(current_path, path);
253253
}
254254
None => return layout::redirect(&format!("{}{}", self.root_path(), path)),

src/librustdoc/html/render/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use rustc_span::{
6363
use serde::ser::SerializeSeq;
6464
use serde::{Serialize, Serializer};
6565

66+
use crate::clean::utils::join_path_segments;
6667
use crate::clean::{self, ItemId, RenderedLink, SelfTy};
6768
use crate::error::Error;
6869
use crate::formats::cache::Cache;
@@ -2524,7 +2525,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
25242525
let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
25252526

25262527
if let Some(path) = fqp {
2527-
out.push(path.join("::"));
2528+
out.push(join_path_segments(&path));
25282529
}
25292530
};
25302531

src/librustdoc/html/render/print_item.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use super::{
2222
ImplRenderingParameters,
2323
};
2424
use crate::clean;
25+
use crate::clean::utils::join_path_segments_with_sep;
2526
use crate::formats::item_type::ItemType;
2627
use crate::formats::{AssocItemRender, Impl, RenderMode};
2728
use crate::html::escape::Escape;
@@ -874,7 +875,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
874875
cx.current.join("/")
875876
} else {
876877
let (ref path, _) = cache.external_paths[&it.def_id.expect_def_id()];
877-
path[..path.len() - 1].join("/")
878+
join_path_segments_with_sep(&path[..path.len() - 1], "/")
878879
},
879880
ty = it.type_(),
880881
name = *it.name.as_ref().unwrap()

src/librustdoc/html/render/write_shared.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ pub(super) fn write_shared(
569569

570570
let mut mydst = dst.clone();
571571
for part in &remote_path[..remote_path.len() - 1] {
572-
mydst.push(part);
572+
mydst.push(&*part.as_str());
573573
}
574574
cx.shared.ensure_dir(&mydst)?;
575575
mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1]));

src/librustdoc/html/url_parts_builder.rs

+19
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use rustc_span::Symbol;
2+
13
/// A builder that allows efficiently and easily constructing the part of a URL
24
/// after the domain: `nightly/core/str/struct.Bytes.html`.
35
///
@@ -115,5 +117,22 @@ impl<'a> Extend<&'a str> for UrlPartsBuilder {
115117
}
116118
}
117119

120+
impl FromIterator<Symbol> for UrlPartsBuilder {
121+
fn from_iter<T: IntoIterator<Item = Symbol>>(iter: T) -> Self {
122+
let iter = iter.into_iter();
123+
let mut builder = Self::with_capacity_bytes(AVG_PART_LENGTH * iter.size_hint().0);
124+
iter.for_each(|part| builder.push(&part.as_str()));
125+
builder
126+
}
127+
}
128+
129+
impl Extend<Symbol> for UrlPartsBuilder {
130+
fn extend<T: IntoIterator<Item = Symbol>>(&mut self, iter: T) {
131+
let iter = iter.into_iter();
132+
self.buf.reserve(AVG_PART_LENGTH * iter.size_hint().0);
133+
iter.for_each(|part| self.push(&part.as_str()));
134+
}
135+
}
136+
118137
#[cfg(test)]
119138
mod tests;

src/librustdoc/json/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ impl JsonRenderer<'tcx> {
121121
})
122122
.0
123123
.last()
124-
.map(Clone::clone),
124+
.map(|s| s.to_string()),
125125
visibility: types::Visibility::Public,
126126
inner: types::ItemEnum::Trait(trait_item.clone().into_tcx(self.tcx)),
127127
span: None,
@@ -231,7 +231,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
231231
from_item_id(k.into()),
232232
types::ItemSummary {
233233
crate_id: k.krate.as_u32(),
234-
path,
234+
path: path.iter().map(|s| s.to_string()).collect(),
235235
kind: kind.into_tcx(self.tcx),
236236
},
237237
)

src/librustdoc/visit_ast.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,9 @@ impl Module<'hir> {
4242
}
4343

4444
// FIXME: Should this be replaced with tcx.def_path_str?
45-
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<String> {
46-
let crate_name = tcx.crate_name(did.krate).to_string();
47-
let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
48-
// extern blocks have an empty name
49-
let s = elem.data.to_string();
50-
if !s.is_empty() { Some(s) } else { None }
51-
});
45+
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<Symbol> {
46+
let crate_name = tcx.crate_name(did.krate);
47+
let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| elem.data.get_opt_name());
5248
std::iter::once(crate_name).chain(relative).collect()
5349
}
5450

@@ -71,7 +67,7 @@ crate struct RustdocVisitor<'a, 'tcx> {
7167
inlining: bool,
7268
/// Are the current module and all of its parents public?
7369
inside_public_path: bool,
74-
exact_paths: FxHashMap<DefId, Vec<String>>,
70+
exact_paths: FxHashMap<DefId, Vec<Symbol>>,
7571
}
7672

7773
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {

0 commit comments

Comments
 (0)