Skip to content

Commit 53439ce

Browse files
Auto merge of #147402 - yotamofek:wip/rustdoc/search_index/impl-display, r=<try>
[PERF] [rustdoc] Try to speed up search index building
2 parents d2acb42 + 5ea7e13 commit 53439ce

File tree

4 files changed

+271
-282
lines changed

4 files changed

+271
-282
lines changed

src/librustdoc/display.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ impl Wrapped<char> {
8282
pub(crate) fn with_square_brackets() -> Self {
8383
Self { prefix: '[', suffix: ']' }
8484
}
85+
86+
pub(crate) fn with_curly_brackets() -> Self {
87+
Self { prefix: '{', suffix: '}' }
88+
}
8589
}
8690

8791
impl<T: Display> Wrapped<T> {

src/librustdoc/html/render/mod.rs

Lines changed: 156 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,15 @@ use rustc_middle::ty::print::PrintTraitRefExt;
6161
use rustc_middle::ty::{self, TyCtxt};
6262
use rustc_span::symbol::{Symbol, sym};
6363
use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
64+
use serde::ser::SerializeSeq as _;
65+
use serde::{Deserialize, Serialize};
6466
use tracing::{debug, info};
6567

6668
pub(crate) use self::context::*;
6769
pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources};
6870
pub(crate) use self::write_shared::*;
6971
use crate::clean::{self, ItemId, RenderedLink};
70-
use crate::display::{Joined as _, MaybeDisplay as _};
72+
use crate::display::{Joined as _, MaybeDisplay as _, Wrapped};
7173
use crate::error::Error;
7274
use crate::formats::Impl;
7375
use crate::formats::cache::Cache;
@@ -144,7 +146,7 @@ pub(crate) struct IndexItem {
144146
}
145147

146148
/// A type used for the search index.
147-
#[derive(Debug, Eq, PartialEq)]
149+
#[derive(Clone, Debug, Eq, PartialEq)]
148150
struct RenderType {
149151
id: Option<RenderTypeId>,
150152
generics: Option<Vec<RenderType>>,
@@ -153,59 +155,35 @@ struct RenderType {
153155

154156
impl RenderType {
155157
fn size(&self) -> usize {
156-
let mut size = 1;
157-
if let Some(generics) = &self.generics {
158-
size += generics.iter().map(RenderType::size).sum::<usize>();
159-
}
160-
if let Some(bindings) = &self.bindings {
161-
for (_, constraints) in bindings.iter() {
162-
size += 1;
163-
size += constraints.iter().map(RenderType::size).sum::<usize>();
164-
}
165-
}
166-
size
158+
self.bindings
159+
.iter()
160+
.flatten()
161+
.map(|(_, constraints)| constraints)
162+
.chain(self.generics.iter())
163+
.map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>() + 1)
164+
.sum()
167165
}
168-
// Types are rendered as lists of lists, because that's pretty compact.
169-
// The contents of the lists are always integers in self-terminating hex
170-
// form, handled by `RenderTypeId::write_to_string`, so no commas are
171-
// needed to separate the items.
172-
fn write_to_string(&self, string: &mut String) {
173-
fn write_optional_id(id: Option<RenderTypeId>, string: &mut String) {
174-
// 0 is a sentinel, everything else is one-indexed
175-
match id {
176-
Some(id) => id.write_to_string(string),
177-
None => string.push('`'),
178-
}
179-
}
180-
// Either just the type id, or `{type, generics, bindings?}`
181-
// where generics is a list of types,
182-
// and bindings is a list of `{id, typelist}` pairs.
183-
if self.generics.is_some() || self.bindings.is_some() {
184-
string.push('{');
185-
write_optional_id(self.id, string);
186-
string.push('{');
187-
for generic in self.generics.as_deref().unwrap_or_default() {
188-
generic.write_to_string(string);
189-
}
190-
string.push('}');
191-
if self.bindings.is_some() {
192-
string.push('{');
193-
for binding in self.bindings.as_deref().unwrap_or_default() {
194-
string.push('{');
195-
binding.0.write_to_string(string);
196-
string.push('{');
197-
for constraint in &binding.1[..] {
198-
constraint.write_to_string(string);
199-
}
200-
string.push_str("}}");
201-
}
202-
string.push('}');
166+
167+
fn write_bindings(&self) -> Option<impl fmt::Display> {
168+
let Some(bindings) = &self.bindings else {
169+
return None;
170+
};
171+
172+
Some(Wrapped::with_curly_brackets().wrap_fn(move |f| {
173+
for (binding, constraints) in bindings {
174+
Wrapped::with_curly_brackets()
175+
.wrap_fn(|f| {
176+
binding.write_to_string().fmt(f)?;
177+
Wrapped::with_curly_brackets()
178+
.wrap_fn(|f| constraints.iter().joined("", f))
179+
.fmt(f)
180+
})
181+
.fmt(f)?;
203182
}
204-
string.push('}');
205-
} else {
206-
write_optional_id(self.id, string);
207-
}
183+
Ok(())
184+
}))
208185
}
186+
209187
fn read_from_bytes(string: &[u8]) -> (RenderType, usize) {
210188
let mut i = 0;
211189
if string[i] == b'{' {
@@ -264,6 +242,40 @@ impl RenderType {
264242
}
265243
}
266244

245+
impl fmt::Display for RenderType {
246+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247+
// Types are rendered as lists of lists, because that's pretty compact.
248+
// The contents of the lists are always integers in self-terminating hex
249+
// form, handled by `RenderTypeId::write_to_string`, so no commas are
250+
// needed to separate the items.
251+
252+
fn write_optional_id(id: Option<RenderTypeId>) -> impl fmt::Display {
253+
// 0 is a sentinel, everything else is one-indexed
254+
match id {
255+
Some(id) => Either::Left(id.write_to_string()),
256+
None => Either::Right('`'),
257+
}
258+
}
259+
260+
// Either just the type id, or `{type, generics, bindings?}`
261+
// where generics is a list of types,
262+
// and bindings is a list of `{id, typelist}` pairs.
263+
if self.generics.is_some() || self.bindings.is_some() {
264+
Wrapped::with_curly_brackets()
265+
.wrap_fn(|f| {
266+
let id = write_optional_id(self.id);
267+
let generics = Wrapped::with_curly_brackets()
268+
.wrap_fn(|f| self.generics.iter().flatten().joined("", f));
269+
let bindings = self.write_bindings().maybe_display();
270+
write!(f, "{id}{generics}{bindings}")
271+
})
272+
.fmt(f)
273+
} else {
274+
write_optional_id(self.id).fmt(f)
275+
}
276+
}
277+
}
278+
267279
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
268280
enum RenderTypeId {
269281
DefId(DefId),
@@ -274,7 +286,7 @@ enum RenderTypeId {
274286
}
275287

276288
impl RenderTypeId {
277-
fn write_to_string(&self, string: &mut String) {
289+
fn write_to_string(&self) -> impl fmt::Display + use<> {
278290
let id: i32 = match &self {
279291
// 0 is a sentinel, everything else is one-indexed
280292
// concrete type
@@ -283,7 +295,7 @@ impl RenderTypeId {
283295
RenderTypeId::Index(idx) => (*idx).try_into().unwrap(),
284296
_ => panic!("must convert render types to indexes before serializing"),
285297
};
286-
search_index::encode::write_signed_vlqhex_to_string(id, string);
298+
search_index::encode::write_signed_vlqhex_to_string(id)
287299
}
288300
fn read_from_bytes(string: &[u8]) -> (Option<RenderTypeId>, usize) {
289301
let Some((value, offset)) = search_index::encode::read_signed_vlqhex_from_string(string)
@@ -301,7 +313,7 @@ impl RenderTypeId {
301313
}
302314

303315
/// Full type of functions/methods in the search index.
304-
#[derive(Debug, Eq, PartialEq)]
316+
#[derive(Clone, Debug, Eq, PartialEq)]
305317
pub(crate) struct IndexItemFunctionType {
306318
inputs: Vec<RenderType>,
307319
output: Vec<RenderType>,
@@ -311,13 +323,13 @@ pub(crate) struct IndexItemFunctionType {
311323

312324
impl IndexItemFunctionType {
313325
fn size(&self) -> usize {
314-
self.inputs.iter().map(RenderType::size).sum::<usize>()
315-
+ self.output.iter().map(RenderType::size).sum::<usize>()
316-
+ self
317-
.where_clause
318-
.iter()
319-
.map(|constraints| constraints.iter().map(RenderType::size).sum::<usize>())
320-
.sum::<usize>()
326+
self.where_clause
327+
.iter()
328+
.flatten()
329+
.chain(&self.inputs)
330+
.chain(&self.output)
331+
.map(RenderType::size)
332+
.sum()
321333
}
322334
fn read_from_string_without_param_names(string: &[u8]) -> (IndexItemFunctionType, usize) {
323335
let mut i = 0;
@@ -367,7 +379,7 @@ impl IndexItemFunctionType {
367379
i += 1;
368380
(IndexItemFunctionType { inputs, output, where_clause, param_names: Vec::new() }, i)
369381
}
370-
fn write_to_string_without_param_names<'a>(&'a self, string: &mut String) {
382+
fn write_to_string_without_param_names<'a>(&'a self) -> impl fmt::Display {
371383
// If we couldn't figure out a type, just write 0,
372384
// which is encoded as `` ` `` (see RenderTypeId::write_to_string).
373385
let has_missing = self
@@ -376,50 +388,94 @@ impl IndexItemFunctionType {
376388
.chain(self.output.iter())
377389
.any(|i| i.id.is_none() && i.generics.is_none());
378390
if has_missing {
379-
string.push('`');
391+
Either::Left('`')
380392
} else {
381-
string.push('{');
382-
match &self.inputs[..] {
383-
[one] if one.generics.is_none() && one.bindings.is_none() => {
384-
one.write_to_string(string);
393+
Either::Right(Wrapped::with_curly_brackets().wrap_fn(|f| {
394+
fn write_render_types(types: &[RenderType]) -> impl fmt::Display {
395+
Wrapped::with_curly_brackets()
396+
.when(!matches!(types, [one] if one.generics.is_none() && one.bindings.is_none()))
397+
.wrap_fn(|f| types.iter().joined("", f))
385398
}
386-
_ => {
387-
string.push('{');
388-
for item in &self.inputs[..] {
389-
item.write_to_string(string);
390-
}
391-
string.push('}');
399+
400+
write_render_types(&self.inputs).fmt(f)?;
401+
if !(self.output.is_empty() && self.where_clause.is_empty()) {
402+
write_render_types(&self.output).fmt(f)?;
392403
}
404+
self.where_clause
405+
.iter()
406+
.map(|constraints| write_render_types(constraints))
407+
.joined("", f)?;
408+
409+
Ok(())
410+
}))
411+
}
412+
}
413+
}
414+
415+
impl Serialize for IndexItemFunctionType {
416+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
417+
where
418+
S: serde::Serializer,
419+
{
420+
let mut seq = serializer.serialize_seq(Some(2))?;
421+
seq.serialize_element(&self.write_to_string_without_param_names().to_string())?;
422+
423+
struct ParamNames<'a>(&'a [Option<Symbol>]);
424+
425+
impl<'a> Serialize for ParamNames<'a> {
426+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
427+
where
428+
S: serde::Serializer,
429+
{
430+
serializer.collect_seq(
431+
self.0
432+
.iter()
433+
.map(|symbol| symbol.as_ref().map(ToString::to_string).unwrap_or_default()),
434+
)
393435
}
394-
match &self.output[..] {
395-
[] if self.where_clause.is_empty() => {}
396-
[one] if one.generics.is_none() && one.bindings.is_none() => {
397-
one.write_to_string(string);
398-
}
399-
_ => {
400-
string.push('{');
401-
for item in &self.output[..] {
402-
item.write_to_string(string);
403-
}
404-
string.push('}');
405-
}
436+
}
437+
438+
seq.serialize_element(&ParamNames(&self.param_names))?;
439+
seq.end()
440+
}
441+
}
442+
443+
impl<'de> Deserialize<'de> for IndexItemFunctionType {
444+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
445+
where
446+
D: serde::Deserializer<'de>,
447+
{
448+
use serde::de::{self, Error as _};
449+
450+
struct FunctionDataVisitor;
451+
impl<'de> de::Visitor<'de> for FunctionDataVisitor {
452+
type Value = IndexItemFunctionType;
453+
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> fmt::Result {
454+
write!(formatter, "fn data")
406455
}
407-
for constraint in &self.where_clause {
408-
if let [one] = &constraint[..]
409-
&& one.generics.is_none()
410-
&& one.bindings.is_none()
411-
{
412-
one.write_to_string(string);
413-
} else {
414-
string.push('{');
415-
for item in &constraint[..] {
416-
item.write_to_string(string);
417-
}
418-
string.push('}');
419-
}
456+
fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<Self::Value, A::Error> {
457+
let (mut function_signature, _) = v
458+
.next_element()?
459+
.map(|fn_: String| {
460+
IndexItemFunctionType::read_from_string_without_param_names(fn_.as_bytes())
461+
})
462+
.ok_or_else(|| A::Error::missing_field("function_signature"))?;
463+
let param_names: Vec<Option<Symbol>> = v
464+
.next_element()?
465+
.map(|param_names: Vec<String>| {
466+
param_names
467+
.into_iter()
468+
.map(|symbol| {
469+
if symbol.is_empty() { None } else { Some(Symbol::intern(&symbol)) }
470+
})
471+
.collect()
472+
})
473+
.ok_or_else(|| A::Error::missing_field("param_names"))?;
474+
function_signature.param_names = param_names;
475+
Ok(function_signature)
420476
}
421-
string.push('}');
422477
}
478+
deserializer.deserialize_any(FunctionDataVisitor)
423479
}
424480
}
425481

0 commit comments

Comments
 (0)