Skip to content

Commit c3cd657

Browse files
committed
rustdoc-search: intern function search types
This takes advantage of more reuse opportunities. Along with the empty object commit, they bringing memory usage down about 20% over the original.
1 parent e61be1b commit c3cd657

File tree

1 file changed

+72
-17
lines changed

1 file changed

+72
-17
lines changed

src/librustdoc/html/static/js/search.js

+72-17
Original file line numberDiff line numberDiff line change
@@ -2725,17 +2725,25 @@ ${item.displayPath}<span class="${type}">${name}</span>\
27252725
/**
27262726
* Empty, immutable map used in item search types with no bindings.
27272727
*
2728-
* @type {Map<integer, Array<Functiontype>>}
2728+
* @type {Map<number, Array<FunctionType>>}
27292729
*/
27302730
const EMPTY_BINDINGS_MAP = new Map();
27312731

27322732
/**
27332733
* Empty, immutable map used in item search types with no bindings.
27342734
*
2735-
* @type {Array<Functiontype>}
2735+
* @type {Array<FunctionType>}
27362736
*/
27372737
const EMPTY_GENERICS_ARRAY = [];
27382738

2739+
/**
2740+
* Object pool for function types with no bindings or generics.
2741+
* This is reset after loading the index.
2742+
*
2743+
* @type {Map<number|null, FunctionType>}
2744+
*/
2745+
let TYPES_POOL = new Map();
2746+
27392747
/**
27402748
* Converts a single type.
27412749
*
@@ -2778,35 +2786,80 @@ ${item.displayPath}<span class="${type}">${name}</span>\
27782786
bindings = EMPTY_BINDINGS_MAP;
27792787
}
27802788
}
2789+
/**
2790+
* @type {FunctionType}
2791+
*/
2792+
let result;
27812793
if (pathIndex < 0) {
27822794
// types less than 0 are generic parameters
27832795
// the actual names of generic parameters aren't stored, since they aren't API
2784-
return {
2796+
result = {
27852797
id: pathIndex,
27862798
ty: TY_GENERIC,
27872799
path: null,
27882800
generics,
27892801
bindings,
27902802
};
2791-
}
2792-
if (pathIndex === 0) {
2803+
} else if (pathIndex === 0) {
27932804
// `0` is used as a sentinel because it's fewer bytes than `null`
2794-
return {
2805+
result = {
27952806
id: null,
27962807
ty: null,
27972808
path: null,
27982809
generics,
27992810
bindings,
28002811
};
2812+
} else {
2813+
const item = lowercasePaths[pathIndex - 1];
2814+
result = {
2815+
id: buildTypeMapIndex(item.name, isAssocType),
2816+
ty: item.ty,
2817+
path: item.path,
2818+
generics,
2819+
bindings,
2820+
};
28012821
}
2802-
const item = lowercasePaths[pathIndex - 1];
2803-
return {
2804-
id: buildTypeMapIndex(item.name, isAssocType),
2805-
ty: item.ty,
2806-
path: item.path,
2807-
generics,
2808-
bindings,
2809-
};
2822+
const cr = TYPES_POOL.get(result.id);
2823+
if (cr) {
2824+
// Shallow equality check. Since this function is used
2825+
// to construct every type object, this should be mostly
2826+
// equivalent to a deep equality check, except if there's
2827+
// a conflict, we don't keep the old one around, so it's
2828+
// not a fully precise implementation of hashcons.
2829+
if (cr.generics.length === result.generics.length &&
2830+
cr.generics !== result.generics &&
2831+
cr.generics.every((x, i) => result.generics[i] === x)
2832+
) {
2833+
result.generics = cr.generics;
2834+
}
2835+
if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) {
2836+
let ok = true;
2837+
for (const [k, v] of cr.bindings.entries()) {
2838+
const v2 = result.bindings.get(v);
2839+
if (!v2) {
2840+
ok = false;
2841+
break;
2842+
}
2843+
if (v !== v2 && v.length === v2.length && v.every((x, i) => v2[i] === x)) {
2844+
result.bindings.set(k, v);
2845+
} else if (v !== v2) {
2846+
ok = false;
2847+
break;
2848+
}
2849+
}
2850+
if (ok) {
2851+
result.bindings = cr.bindings;
2852+
}
2853+
}
2854+
if (cr.ty === result.ty && cr.path === result.path
2855+
&& cr.bindings === result.bindings && cr.generics === result.generics
2856+
&& cr.ty === result.ty
2857+
) {
2858+
return cr;
2859+
}
2860+
}
2861+
TYPES_POOL.set(result.id, result);
2862+
return result;
28102863
}
28112864

28122865
/**
@@ -2817,7 +2870,7 @@ ${item.displayPath}<span class="${type}">${name}</span>\
28172870
* object-based encoding so that the actual search code is more readable and easier to debug.
28182871
*
28192872
* The raw function search type format is generated using serde in
2820-
* librustdoc/html/render/mod.rs: impl Serialize for IndexItemFunctionType
2873+
* librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string
28212874
*
28222875
* @param {{
28232876
* string: string,
@@ -2986,8 +3039,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
29863039
const fb = {
29873040
id: null,
29883041
ty: 0,
2989-
generics: [],
2990-
bindings: new Map(),
3042+
generics: EMPTY_GENERICS_ARRAY,
3043+
bindings: EMPTY_BINDINGS_MAP,
29913044
};
29923045
for (const [k, v] of type.bindings.entries()) {
29933046
fb.id = k;
@@ -3215,6 +3268,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
32153268
}
32163269
currentIndex += itemTypes.length;
32173270
}
3271+
// Drop the (rather large) hash table used for reusing function items
3272+
TYPES_POOL = new Map();
32183273
}
32193274

32203275
/**

0 commit comments

Comments
 (0)