diff --git a/Cargo.lock b/Cargo.lock index e528927..896afa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,7 +158,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -287,7 +287,7 @@ checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -781,7 +781,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -790,7 +790,25 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -799,13 +817,29 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -814,38 +848,86 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/src/indexed_crate.rs b/src/indexed_crate.rs index fca72a2..e7fbe81 100644 --- a/src/indexed_crate.rs +++ b/src/indexed_crate.rs @@ -1,6 +1,6 @@ use std::{ borrow::Borrow, - collections::{BTreeSet, HashMap}, + collections::{hash_map::Entry, HashMap, HashSet}, }; use rustdoc_types::{Crate, Id, Item}; @@ -41,6 +41,138 @@ pub struct IndexedCrate<'a> { pub(crate) manually_inlined_builtin_traits: HashMap, } +/// Map a Key to a List (Vec) of values +/// +/// It also has some nice operations for pushing a value to the list, or extending the list with +/// many values. +struct MapList(HashMap>); + +impl FromIterator<(K, V)> for MapList { + #[inline] + fn from_iter>(iter: T) -> Self { + // We could use Iterator::size_hint here to preallocate some space, but I couldn't measure + // a perf inprovement from that. + let mut map = Self::new(); + for (key, value) in iter { + map.insert(key, value); + } + map + } +} + +impl Extend<(K, V)> for MapList { + #[inline] + fn extend>(&mut self, iter: T) { + // We could use Iterator::size_hint here to reserve some space, but I measured a 2%-3% + // regression when doing that. + for (key, value) in iter.into_iter() { + self.insert(key, value); + } + } +} + +impl MapList { + #[inline] + pub fn new() -> Self { + Self(HashMap::new()) + } + + #[inline] + pub fn into_inner(self) -> HashMap> { + self.0 + } + + #[inline] + pub fn insert(&mut self, key: K, value: V) { + match self.0.entry(key) { + Entry::Occupied(mut entry) => entry.get_mut().push(value), + Entry::Vacant(entry) => { + entry.insert(vec![value]); + } + } + } +} + +/// Build the impl index +/// +/// When compiled using the `rayon` feature, build it in parallel. Specifically, this paralelizes +/// the work of gathering all of the impls for the items in the index. +fn build_impl_index(index: &HashMap) -> MapList, (&Item, &Item)> { + let iter = index.iter(); + iter.filter_map(|(id, item)| { + let impls = match &item.inner { + rustdoc_types::ItemEnum::Struct(s) => s.impls.as_slice(), + rustdoc_types::ItemEnum::Enum(e) => e.impls.as_slice(), + rustdoc_types::ItemEnum::Union(u) => u.impls.as_slice(), + _ => return None, + }; + + let iter = impls.iter(); + + Some((id, iter.filter_map(|impl_id| index.get(impl_id)))) + }) + .flat_map(|(id, impl_items)| { + impl_items.flat_map(move |impl_item| { + let impl_inner = match &impl_item.inner { + rustdoc_types::ItemEnum::Impl(impl_inner) => impl_inner, + _ => unreachable!("expected impl but got another item type: {impl_item:?}"), + }; + let trait_provided_methods: HashSet<_> = impl_inner + .provided_trait_methods + .iter() + .map(|x| x.as_str()) + .collect(); + + let trait_items = impl_inner + .trait_ + .as_ref() + .and_then(|trait_path| index.get(&trait_path.id)) + .map(move |trait_item| { + if let rustdoc_types::ItemEnum::Trait(trait_item) = &trait_item.inner { + trait_item.items.as_slice() + } else { + &[] + } + }) + .unwrap_or(&[]); + + let trait_items = trait_items.iter(); + + let trait_provided_items = trait_items + .filter_map(|id| index.get(id)) + .filter(move |item| { + item.name + .as_deref() + .map(|name| trait_provided_methods.contains(name)) + .unwrap_or_default() + }) + .map(move |provided_item| { + ( + ImplEntry::new( + id, + provided_item + .name + .as_deref() + .expect("item should have had a name"), + ), + (impl_item, provided_item), + ) + }); + + let impl_items = impl_inner.items.iter(); + + impl_items + .filter_map(move |item_id| { + let item = index.get(item_id)?; + let item_name = item.name.as_deref()?; + Some((ImplEntry::new(id, item_name), (impl_item, item))) + }) + .chain(trait_provided_items) + }) + }) + .collect() +} + impl<'a> IndexedCrate<'a> { pub fn new(crate_: &'a Crate) -> Self { let mut value = Self { @@ -51,95 +183,28 @@ impl<'a> IndexedCrate<'a> { impl_index: None, }; - let mut imports_index: HashMap> = - HashMap::with_capacity(crate_.index.len()); - for item in crate_ - .index - .values() - .filter(|item| supported_item_kind(item)) - { - for importable_path in value.publicly_importable_names(&item.id) { - let modifiers = importable_path.modifiers; - - imports_index - .entry(importable_path.path) - .or_default() - .push((item, modifiers)); - } - } - let index_size = imports_index.len(); - value.imports_index = Some(imports_index); - - let mut impl_index: HashMap, Vec<(&'a Item, &'a Item)>> = - HashMap::with_capacity(index_size); - for (id, impl_items) in crate_.index.iter().filter_map(|(id, item)| { - let impls = match &item.inner { - rustdoc_types::ItemEnum::Struct(s) => &s.impls, - rustdoc_types::ItemEnum::Enum(e) => &e.impls, - rustdoc_types::ItemEnum::Union(u) => &u.impls, - _ => return None, - }; - - let impl_items = impls.iter().filter_map(|impl_id| crate_.index.get(impl_id)); - - Some((id, impl_items)) - }) { - for impl_item in impl_items { - let impl_inner = match &impl_item.inner { - rustdoc_types::ItemEnum::Impl(impl_inner) => impl_inner, - _ => unreachable!("expected impl but got another item type: {impl_item:?}"), - }; - let trait_provided_methods: BTreeSet<_> = impl_inner - .provided_trait_methods - .iter() - .map(|x| x.as_str()) - .collect(); - if let Some(trait_item) = impl_inner - .trait_ - .as_ref() - .and_then(|trait_path| crate_.index.get(&trait_path.id)) - { - if let rustdoc_types::ItemEnum::Trait(trait_item) = &trait_item.inner { - for provided_item in trait_item - .items - .iter() - .filter_map(|id| crate_.index.get(id)) - .filter(|item| { - item.name - .as_deref() - .map(|name| trait_provided_methods.contains(name)) - .unwrap_or_default() - }) - { - impl_index - .entry(ImplEntry::new( - id, - provided_item - .name - .as_deref() - .expect("item should have had a name"), - )) - .or_default() - .push((impl_item, provided_item)); - } - } - } - - for contained_item in impl_inner - .items - .iter() - .filter_map(|item_id| crate_.index.get(item_id)) - { - if let Some(contained_item_name) = contained_item.name.as_deref() { - impl_index - .entry(ImplEntry::new(id, contained_item_name)) - .or_default() - .push((impl_item, contained_item)); + // Build the imports index + // + // This is inlined because we need access to `value`, but `value` is not a valid + // `IndexedCrate` yet. Do not extract into a separate function. + value.imports_index = Some( + crate_ + .index + .iter() + .filter_map(|(_id, item)| { + if !supported_item_kind(item) { + return None; } - } - } - } - value.impl_index = Some(impl_index); + let importable_paths = value.publicly_importable_names(&item.id); + Some(importable_paths.into_iter().map(move |importable_path| { + (importable_path.path, (item, importable_path.modifiers)) + })) + }) + .flatten() + .collect::>() + .into_inner(), + ); + value.impl_index = Some(build_impl_index(&crate_.index).into_inner()); value }