Skip to content

Commit 86b971b

Browse files
authored
Rollup merge of #83003 - notriddle:rustdoc-index-v3, r=GuillaumeGomez
rustdoc: tweak the search index format This essentially switches search-index.js from a "array of struct" to a "struct of array" format, like this: { "doc": "Crate documentation", "t": [ 1, 1, 2, 3, ... ], "n": [ "Something", "SomethingElse", "whatever", "do_stuff", ... ], "q": [ "a::b", "", "", "", ... ], "d": [ "A Struct That Does Something", "Another Struct", "a function", "another function", ... ], "i": [ 0, 0, 1, 1, ... ], "f": [ null, null, [], [], ... ], "p": ..., "a": ... } So `{ty: 1, name: "Something", path: "a::b", desc: "A Struct That Does Something", parent_idx: 0, search_type: null}` is the first item. This makes the uncompressed version smaller, but it really shows on the compressed version: notriddle:rust$ wc -c new-search-index1.52.0.js 2622427 new-search-index1.52.0.js notriddle:rust$ wc -c old-search-index1.52.0.js 2725046 old-search-index1.52.0.js notriddle:rust$ gzip new-search-index1.52.0.js notriddle:rust$ gzip old-search-index1.52.0.js notriddle:rust$ wc -c new-search-index1.52.0.js.gz 239385 new-search-index1.52.0.js.gz notriddle:rust$ wc -c old-search-index1.52.0.js.gz 296328 old-search-index1.52.0.js.gz That's a 4% improvement on the uncompressed version (fewer `[]`, and also changing `null` to `0` in the parent_idx list), and 20% improvement after gzipping it, thanks to putting like-typed data next to each other. Any compression algorithm based on a sliding window will probably show this kind of improvement.
2 parents 6a7953d + 3934dd1 commit 86b971b

File tree

3 files changed

+74
-42
lines changed

3 files changed

+74
-42
lines changed

src/librustdoc/html/render/cache.rs

+54-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::Path;
44
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
55
use rustc_middle::ty::TyCtxt;
66
use rustc_span::symbol::{sym, Symbol};
7-
use serde::Serialize;
7+
use serde::ser::{Serialize, SerializeStruct, Serializer};
88

99
use crate::clean::types::{
1010
FnDecl, FnRetTy, GenericBound, Generics, GetDefId, Type, TypeKind, WherePredicate,
@@ -133,21 +133,69 @@ crate fn build_index<'tcx>(krate: &clean::Crate, cache: &mut Cache, tcx: TyCtxt<
133133
.map(|module| module.doc_value().map_or_else(String::new, |s| short_markdown_summary(&s)))
134134
.unwrap_or_default();
135135

136-
#[derive(Serialize)]
137136
struct CrateData<'a> {
138137
doc: String,
139-
#[serde(rename = "i")]
140138
items: Vec<&'a IndexItem>,
141-
#[serde(rename = "p")]
142139
paths: Vec<(ItemType, String)>,
143140
// The String is alias name and the vec is the list of the elements with this alias.
144141
//
145142
// To be noted: the `usize` elements are indexes to `items`.
146-
#[serde(rename = "a")]
147-
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
148143
aliases: &'a BTreeMap<String, Vec<usize>>,
149144
}
150145

146+
impl<'a> Serialize for CrateData<'a> {
147+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
148+
where
149+
S: Serializer,
150+
{
151+
let has_aliases = !self.aliases.is_empty();
152+
let mut crate_data =
153+
serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
154+
crate_data.serialize_field("doc", &self.doc)?;
155+
crate_data.serialize_field(
156+
"t",
157+
&self.items.iter().map(|item| &item.ty).collect::<Vec<_>>(),
158+
)?;
159+
crate_data.serialize_field(
160+
"n",
161+
&self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
162+
)?;
163+
crate_data.serialize_field(
164+
"q",
165+
&self.items.iter().map(|item| &item.path).collect::<Vec<_>>(),
166+
)?;
167+
crate_data.serialize_field(
168+
"d",
169+
&self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
170+
)?;
171+
crate_data.serialize_field(
172+
"i",
173+
&self
174+
.items
175+
.iter()
176+
.map(|item| {
177+
assert_eq!(
178+
item.parent.is_some(),
179+
item.parent_idx.is_some(),
180+
"`{}` is missing idx",
181+
item.name
182+
);
183+
item.parent_idx.map(|x| x + 1).unwrap_or(0)
184+
})
185+
.collect::<Vec<_>>(),
186+
)?;
187+
crate_data.serialize_field(
188+
"f",
189+
&self.items.iter().map(|item| &item.search_type).collect::<Vec<_>>(),
190+
)?;
191+
crate_data.serialize_field("p", &self.paths)?;
192+
if has_aliases {
193+
crate_data.serialize_field("a", &self.aliases)?;
194+
}
195+
crate_data.end()
196+
}
197+
}
198+
151199
// Collect the index into a string
152200
format!(
153201
r#""{}":{}"#,

src/librustdoc/html/render/mod.rs

-17
Original file line numberDiff line numberDiff line change
@@ -166,23 +166,6 @@ crate struct IndexItem {
166166
crate search_type: Option<IndexItemFunctionType>,
167167
}
168168

169-
impl Serialize for IndexItem {
170-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171-
where
172-
S: Serializer,
173-
{
174-
assert_eq!(
175-
self.parent.is_some(),
176-
self.parent_idx.is_some(),
177-
"`{}` is missing idx",
178-
self.name
179-
);
180-
181-
(self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type)
182-
.serialize(serializer)
183-
}
184-
}
185-
186169
/// A type used for the search index.
187170
#[derive(Debug)]
188171
crate struct RenderType {

src/librustdoc/html/static/main.js

+20-19
Original file line numberDiff line numberDiff line change
@@ -1847,13 +1847,18 @@ function defocusSearchBar() {
18471847
});
18481848
currentIndex += 1;
18491849

1850-
// an array of [(Number) item type,
1851-
// (String) name,
1852-
// (String) full path or empty string for previous path,
1853-
// (String) description,
1854-
// (Number | null) the parent path index to `paths`]
1855-
// (Object | null) the type of the function (if any)
1856-
var items = rawSearchIndex[crate].i;
1850+
// an array of (Number) item types
1851+
var itemTypes = rawSearchIndex[crate].t;
1852+
// an array of (String) item names
1853+
var itemNames = rawSearchIndex[crate].n;
1854+
// an array of (String) full paths (or empty string for previous path)
1855+
var itemPaths = rawSearchIndex[crate].q;
1856+
// an array of (String) descriptions
1857+
var itemDescs = rawSearchIndex[crate].d;
1858+
// an array of (Number) the parent path index + 1 to `paths`, or 0 if none
1859+
var itemParentIdxs = rawSearchIndex[crate].i;
1860+
// an array of (Object | null) the type of the function, if any
1861+
var itemFunctionSearchTypes = rawSearchIndex[crate].f;
18571862
// an array of [(Number) item type,
18581863
// (String) name]
18591864
var paths = rawSearchIndex[crate].p;
@@ -1867,28 +1872,24 @@ function defocusSearchBar() {
18671872
paths[i] = {ty: paths[i][0], name: paths[i][1]};
18681873
}
18691874

1870-
// convert `items` into an object form, and construct word indices.
1875+
// convert `item*` into an object form, and construct word indices.
18711876
//
18721877
// before any analysis is performed lets gather the search terms to
18731878
// search against apart from the rest of the data. This is a quick
18741879
// operation that is cached for the life of the page state so that
18751880
// all other search operations have access to this cached data for
18761881
// faster analysis operations
1877-
len = items.length;
1882+
len = itemTypes.length;
18781883
var lastPath = "";
18791884
for (i = 0; i < len; ++i) {
1880-
var rawRow = items[i];
1881-
if (!rawRow[2]) {
1882-
rawRow[2] = lastPath;
1883-
}
18841885
var row = {
18851886
crate: crate,
1886-
ty: rawRow[0],
1887-
name: rawRow[1],
1888-
path: rawRow[2],
1889-
desc: rawRow[3],
1890-
parent: paths[rawRow[4]],
1891-
type: rawRow[5],
1887+
ty: itemTypes[i],
1888+
name: itemNames[i],
1889+
path: itemPaths[i] ? itemPaths[i] : lastPath,
1890+
desc: itemDescs[i],
1891+
parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
1892+
type: itemFunctionSearchTypes[i],
18921893
};
18931894
searchIndex.push(row);
18941895
if (typeof row.name === "string") {

0 commit comments

Comments
 (0)