1use std::{any::Any, cell::RefCell, rc::Rc};
2
3use ahash::HashMap;
4use bitvec::vec::BitVec;
5use elsa::FrozenMap;
6use event_listener::Event;
7
8use crate::{
9 Candidates, Dependencies, DependencyProvider, HintDependenciesAvailable, NameId, Requirement,
10 SolvableId, VersionSetId,
11 internal::{
12 arena::{Arena, ArenaId},
13 frozen_copy_map::FrozenCopyMap,
14 id::{CandidatesId, DependenciesId},
15 },
16};
17
18pub struct SolverCache<D: DependencyProvider> {
21 provider: D,
22
23 candidates: Arena<CandidatesId, Candidates>,
25 package_name_to_candidates: FrozenCopyMap<NameId, CandidatesId>,
26 package_name_to_candidates_in_flight: RefCell<HashMap<NameId, Rc<Event>>>,
27
28 version_set_candidates: FrozenMap<VersionSetId, Vec<SolvableId>, ahash::RandomState>,
30
31 version_set_inverse_candidates: FrozenMap<VersionSetId, Vec<SolvableId>, ahash::RandomState>,
35
36 requirement_to_sorted_candidates: FrozenMap<Requirement, Vec<SolvableId>, ahash::RandomState>,
39
40 solvable_dependencies: Arena<DependenciesId, Dependencies>,
42 solvable_to_dependencies: FrozenCopyMap<SolvableId, DependenciesId>,
43
44 hint_dependencies_available: RefCell<BitVec>,
49}
50
51impl<D: DependencyProvider> SolverCache<D> {
52 pub fn new(provider: D) -> Self {
54 Self {
55 provider,
56 candidates: Default::default(),
57 package_name_to_candidates: Default::default(),
58 package_name_to_candidates_in_flight: Default::default(),
59 version_set_candidates: Default::default(),
60 version_set_inverse_candidates: Default::default(),
61 requirement_to_sorted_candidates: Default::default(),
62 solvable_dependencies: Default::default(),
63 solvable_to_dependencies: Default::default(),
64 hint_dependencies_available: Default::default(),
65 }
66 }
67
68 pub fn provider(&self) -> &D {
70 &self.provider
71 }
72
73 pub async fn get_or_cache_candidates(
80 &self,
81 package_name: NameId,
82 ) -> Result<&Candidates, Box<dyn Any>> {
83 let candidates_id = match self.package_name_to_candidates.get_copy(&package_name) {
86 Some(id) => id,
87 None => {
88 if let Some(value) = self.provider.should_cancel_with_value() {
92 return Err(value);
93 }
94
95 let in_flight_request = self
97 .package_name_to_candidates_in_flight
98 .borrow()
99 .get(&package_name)
100 .cloned();
101 match in_flight_request {
102 Some(in_flight) => {
103 in_flight.listen().await;
106 self.package_name_to_candidates
107 .get_copy(&package_name)
108 .expect("after waiting for a request the result should be available")
109 }
110 None => {
111 self.package_name_to_candidates_in_flight
113 .borrow_mut()
114 .insert(package_name, Rc::new(Event::new()));
115
116 let candidates = self
118 .provider
119 .get_candidates(package_name)
120 .await
121 .unwrap_or_default();
122
123 {
126 let mut hint_dependencies_available =
127 self.hint_dependencies_available.borrow_mut();
128 let dependencies_available_candidates =
129 match &candidates.hint_dependencies_available {
130 HintDependenciesAvailable::None => &candidates.candidates[0..0],
131 HintDependenciesAvailable::All => &candidates.candidates,
132 HintDependenciesAvailable::Some(candidates) => candidates,
133 };
134 for hint_candidate in dependencies_available_candidates.iter() {
135 let idx = hint_candidate.to_usize();
136 if hint_dependencies_available.len() <= idx {
137 hint_dependencies_available.resize(idx + 1, false);
138 }
139 hint_dependencies_available.set(idx, true)
140 }
141 }
142
143 let candidates_id = self.candidates.alloc(candidates);
145 self.package_name_to_candidates
146 .insert_copy(package_name, candidates_id);
147
148 let notifier = self
151 .package_name_to_candidates_in_flight
152 .borrow_mut()
153 .remove(&package_name)
154 .expect("notifier should be there");
155 notifier.notify(usize::MAX);
156
157 candidates_id
158 }
159 }
160 }
161 };
162
163 Ok(&self.candidates[candidates_id])
165 }
166
167 pub async fn get_or_cache_matching_candidates(
173 &self,
174 version_set_id: VersionSetId,
175 ) -> Result<&[SolvableId], Box<dyn Any>> {
176 match self.version_set_candidates.get(&version_set_id) {
177 Some(candidates) => Ok(candidates),
178 None => {
179 let package_name_id = self.provider.version_set_name(version_set_id);
180
181 tracing::trace!(
182 "Getting matching candidates for package: {}",
183 self.provider.display_name(package_name_id)
184 );
185
186 let candidates = self.get_or_cache_candidates(package_name_id).await?;
187 tracing::trace!("Got {:?} matching candidates", candidates.candidates.len());
188
189 let matching_candidates = self
190 .provider
191 .filter_candidates(&candidates.candidates, version_set_id, false)
192 .await;
193
194 tracing::trace!(
195 "Filtered {:?} matching candidates",
196 matching_candidates.len()
197 );
198
199 Ok(self
200 .version_set_candidates
201 .insert(version_set_id, matching_candidates))
202 }
203 }
204 }
205
206 pub async fn get_or_cache_non_matching_candidates(
211 &self,
212 version_set_id: VersionSetId,
213 ) -> Result<&[SolvableId], Box<dyn Any>> {
214 match self.version_set_inverse_candidates.get(&version_set_id) {
215 Some(candidates) => Ok(candidates),
216 None => {
217 let package_name_id = self.provider.version_set_name(version_set_id);
218
219 tracing::trace!(
220 "Getting NON-matching candidates for package: {:?}",
221 self.provider.display_name(package_name_id).to_string()
222 );
223
224 let candidates = self.get_or_cache_candidates(package_name_id).await?;
225 tracing::trace!(
226 "Got {:?} NON-matching candidates",
227 candidates.candidates.len()
228 );
229
230 let matching_candidates: Vec<SolvableId> = self
231 .provider
232 .filter_candidates(&candidates.candidates, version_set_id, true)
233 .await
234 .into_iter()
235 .collect();
236
237 tracing::trace!(
238 "Filtered {:?} matching candidates",
239 matching_candidates.len()
240 );
241
242 Ok(self
243 .version_set_inverse_candidates
244 .insert(version_set_id, matching_candidates))
245 }
246 }
247 }
248
249 pub async fn get_or_cache_sorted_candidates(
256 &self,
257 requirement: Requirement,
258 ) -> Result<&[SolvableId], Box<dyn Any>> {
259 match requirement {
260 Requirement::Single(version_set_id) => {
261 self.get_or_cache_sorted_candidates_for_version_set(version_set_id)
262 .await
263 }
264 Requirement::Union(version_set_union_id) => {
265 match self.requirement_to_sorted_candidates.get(&requirement) {
266 Some(candidates) => Ok(candidates),
267 None => {
268 let sorted_candidates = futures::future::try_join_all(
269 self.provider()
270 .version_sets_in_union(version_set_union_id)
271 .map(|version_set_id| {
272 self.get_or_cache_sorted_candidates_for_version_set(
273 version_set_id,
274 )
275 }),
276 )
277 .await?
278 .into_iter()
279 .flatten()
280 .copied()
281 .collect();
282
283 Ok(self
284 .requirement_to_sorted_candidates
285 .insert(requirement, sorted_candidates))
286 }
287 }
288 }
289 }
290 }
291
292 pub(crate) async fn get_or_cache_sorted_candidates_for_version_set(
295 &self,
296 version_set_id: VersionSetId,
297 ) -> Result<&[SolvableId], Box<dyn Any>> {
298 let requirement = version_set_id.into();
299 if let Some(candidates) = self.requirement_to_sorted_candidates.get(&requirement) {
300 return Ok(candidates);
301 }
302
303 let package_name_id = self.provider.version_set_name(version_set_id);
304 tracing::trace!(
305 "Getting sorted matching candidates for package: {:?}",
306 self.provider.display_name(package_name_id).to_string()
307 );
308
309 let matching_candidates = self
310 .get_or_cache_matching_candidates(version_set_id)
311 .await?;
312 let candidates = self.get_or_cache_candidates(package_name_id).await?;
313
314 let mut sorted_candidates = Vec::with_capacity(matching_candidates.len());
316 sorted_candidates.extend_from_slice(matching_candidates);
317 self.provider
318 .sort_candidates(self, &mut sorted_candidates)
319 .await;
320
321 if let Some(favored_id) = candidates.favored {
324 if let Some(pos) = sorted_candidates.iter().position(|&s| s == favored_id) {
325 sorted_candidates[0..=pos].rotate_right(1);
327 }
328 }
329
330 Ok(self
331 .requirement_to_sorted_candidates
332 .insert(requirement, sorted_candidates))
333 }
334
335 pub async fn get_or_cache_dependencies(
341 &self,
342 solvable_id: SolvableId,
343 ) -> Result<&Dependencies, Box<dyn Any>> {
344 let dependencies_id = match self.solvable_to_dependencies.get_copy(&solvable_id) {
345 Some(id) => id,
346 None => {
347 if let Some(value) = self.provider.should_cancel_with_value() {
351 return Err(value);
352 }
353
354 let dependencies = self.provider.get_dependencies(solvable_id).await;
355 let dependencies_id = self.solvable_dependencies.alloc(dependencies);
356 self.solvable_to_dependencies
357 .insert_copy(solvable_id, dependencies_id);
358 dependencies_id
359 }
360 };
361
362 Ok(&self.solvable_dependencies[dependencies_id])
363 }
364
365 pub fn are_dependencies_available_for(&self, solvable: SolvableId) -> bool {
370 if self.solvable_to_dependencies.get_copy(&solvable).is_some() {
371 true
372 } else {
373 let solvable_idx = solvable.to_usize();
374 let hint_dependencies_available = self.hint_dependencies_available.borrow();
375 let value = hint_dependencies_available
376 .get(solvable_idx)
377 .as_deref()
378 .copied();
379 value.unwrap_or(false)
380 }
381 }
382}