1#![allow(missing_docs)]
16
17use std::cmp::max;
18use std::cmp::Ordering;
19use std::cmp::Reverse;
20use std::collections::binary_heap;
21use std::collections::BinaryHeap;
22use std::collections::HashSet;
23use std::iter;
24use std::mem;
25use std::sync::Arc;
26use std::sync::Mutex;
27
28use itertools::Itertools as _;
29use ref_cast::ref_cast_custom;
30use ref_cast::RefCastCustom;
31
32use super::entry::IndexEntry;
33use super::entry::IndexPosition;
34use super::entry::LocalPosition;
35use super::entry::SmallIndexPositionsVec;
36use super::entry::SmallLocalPositionsVec;
37use super::readonly::ReadonlyIndexSegment;
38use super::rev_walk::AncestorsBitSet;
39use super::revset_engine;
40use crate::backend::ChangeId;
41use crate::backend::CommitId;
42use crate::hex_util;
43use crate::index::AllHeadsForGcUnsupported;
44use crate::index::ChangeIdIndex;
45use crate::index::Index;
46use crate::index::IndexError;
47use crate::object_id::HexPrefix;
48use crate::object_id::ObjectId as _;
49use crate::object_id::PrefixResolution;
50use crate::revset::ResolvedExpression;
51use crate::revset::Revset;
52use crate::revset::RevsetEvaluationError;
53use crate::store::Store;
54
55pub(super) trait IndexSegment: Send + Sync {
56 fn num_parent_commits(&self) -> u32;
57
58 fn num_local_commits(&self) -> u32;
59
60 fn parent_file(&self) -> Option<&Arc<ReadonlyIndexSegment>>;
61
62 fn name(&self) -> Option<String>;
63
64 fn commit_id_to_pos(&self, commit_id: &CommitId) -> Option<LocalPosition>;
65
66 fn resolve_neighbor_commit_ids(
69 &self,
70 commit_id: &CommitId,
71 ) -> (Option<CommitId>, Option<CommitId>);
72
73 fn resolve_commit_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId>;
74
75 fn resolve_neighbor_change_ids(
76 &self,
77 change_id: &ChangeId,
78 ) -> (Option<ChangeId>, Option<ChangeId>);
79
80 fn resolve_change_id_prefix(
81 &self,
82 prefix: &HexPrefix,
83 ) -> PrefixResolution<(ChangeId, SmallLocalPositionsVec)>;
84
85 fn generation_number(&self, local_pos: LocalPosition) -> u32;
86
87 fn commit_id(&self, local_pos: LocalPosition) -> CommitId;
88
89 fn change_id(&self, local_pos: LocalPosition) -> ChangeId;
90
91 fn num_parents(&self, local_pos: LocalPosition) -> u32;
92
93 fn parent_positions(&self, local_pos: LocalPosition) -> SmallIndexPositionsVec;
94}
95
96pub(super) type DynIndexSegment = dyn IndexSegment;
97
98pub trait AsCompositeIndex {
101 fn as_composite(&self) -> &CompositeIndex;
103}
104
105impl<T: AsCompositeIndex + ?Sized> AsCompositeIndex for &T {
106 fn as_composite(&self) -> &CompositeIndex {
107 <T as AsCompositeIndex>::as_composite(self)
108 }
109}
110
111impl<T: AsCompositeIndex + ?Sized> AsCompositeIndex for &mut T {
112 fn as_composite(&self) -> &CompositeIndex {
113 <T as AsCompositeIndex>::as_composite(self)
114 }
115}
116
117#[derive(RefCastCustom)]
125#[repr(transparent)]
126pub struct CompositeIndex(DynIndexSegment);
127
128impl CompositeIndex {
129 #[ref_cast_custom]
130 pub(super) const fn new(segment: &DynIndexSegment) -> &Self;
131
132 pub(super) fn ancestor_files_without_local(
134 &self,
135 ) -> impl Iterator<Item = &Arc<ReadonlyIndexSegment>> {
136 let parent_file = self.0.parent_file();
137 iter::successors(parent_file, |file| file.parent_file())
138 }
139
140 pub(super) fn ancestor_index_segments(&self) -> impl Iterator<Item = &DynIndexSegment> {
142 iter::once(&self.0).chain(
143 self.ancestor_files_without_local()
144 .map(|file| file.as_ref() as &DynIndexSegment),
145 )
146 }
147
148 pub fn num_commits(&self) -> u32 {
149 self.0.num_parent_commits() + self.0.num_local_commits()
150 }
151
152 pub fn stats(&self) -> IndexStats {
153 let num_commits = self.num_commits();
154 let mut num_merges = 0;
155 let mut max_generation_number = 0;
156 let mut change_ids = HashSet::new();
157 for pos in 0..num_commits {
158 let entry = self.entry_by_pos(IndexPosition(pos));
159 max_generation_number = max(max_generation_number, entry.generation_number());
160 if entry.num_parents() > 1 {
161 num_merges += 1;
162 }
163 change_ids.insert(entry.change_id());
164 }
165 let num_heads = u32::try_from(self.all_heads_pos().count()).unwrap();
166
167 let mut levels = self
168 .ancestor_index_segments()
169 .map(|segment| IndexLevelStats {
170 num_commits: segment.num_local_commits(),
171 name: segment.name(),
172 })
173 .collect_vec();
174 levels.reverse();
175
176 IndexStats {
177 num_commits,
178 num_merges,
179 max_generation_number,
180 num_heads,
181 num_changes: change_ids.len().try_into().unwrap(),
182 levels,
183 }
184 }
185
186 pub fn entry_by_pos(&self, pos: IndexPosition) -> IndexEntry<'_> {
187 self.ancestor_index_segments()
188 .find_map(|segment| {
189 u32::checked_sub(pos.0, segment.num_parent_commits())
190 .map(|local_pos| IndexEntry::new(segment, pos, LocalPosition(local_pos)))
191 })
192 .unwrap()
193 }
194
195 pub fn entry_by_id(&self, commit_id: &CommitId) -> Option<IndexEntry<'_>> {
196 self.ancestor_index_segments().find_map(|segment| {
197 let local_pos = segment.commit_id_to_pos(commit_id)?;
198 let pos = IndexPosition(local_pos.0 + segment.num_parent_commits());
199 Some(IndexEntry::new(segment, pos, local_pos))
200 })
201 }
202
203 pub fn commit_id_to_pos(&self, commit_id: &CommitId) -> Option<IndexPosition> {
204 self.ancestor_index_segments().find_map(|segment| {
205 let LocalPosition(local_pos) = segment.commit_id_to_pos(commit_id)?;
206 Some(IndexPosition(local_pos + segment.num_parent_commits()))
207 })
208 }
209
210 pub(super) fn resolve_neighbor_commit_ids(
213 &self,
214 commit_id: &CommitId,
215 ) -> (Option<CommitId>, Option<CommitId>) {
216 self.ancestor_index_segments()
217 .map(|segment| segment.resolve_neighbor_commit_ids(commit_id))
218 .reduce(|(acc_prev_id, acc_next_id), (prev_id, next_id)| {
219 (
220 acc_prev_id.into_iter().chain(prev_id).max(),
221 acc_next_id.into_iter().chain(next_id).min(),
222 )
223 })
224 .unwrap()
225 }
226
227 pub(super) fn shortest_unique_change_id_prefix_len(&self, change_id: &ChangeId) -> usize {
230 let (prev_id, next_id) = self.resolve_neighbor_change_ids(change_id);
231 itertools::chain(prev_id, next_id)
232 .map(|id| hex_util::common_hex_len(change_id.as_bytes(), id.as_bytes()) + 1)
233 .max()
234 .unwrap_or(0)
235 }
236
237 pub(super) fn resolve_neighbor_change_ids(
241 &self,
242 change_id: &ChangeId,
243 ) -> (Option<ChangeId>, Option<ChangeId>) {
244 self.ancestor_index_segments()
245 .map(|segment| segment.resolve_neighbor_change_ids(change_id))
246 .reduce(|(acc_prev_id, acc_next_id), (prev_id, next_id)| {
247 (
248 acc_prev_id.into_iter().chain(prev_id).max(),
249 acc_next_id.into_iter().chain(next_id).min(),
250 )
251 })
252 .unwrap()
253 }
254
255 pub(super) fn resolve_change_id_prefix(
260 &self,
261 prefix: &HexPrefix,
262 ) -> PrefixResolution<(ChangeId, SmallIndexPositionsVec)> {
263 use PrefixResolution::*;
264 self.ancestor_index_segments()
265 .fold(NoMatch, |acc_match, segment| {
266 if acc_match == AmbiguousMatch {
267 return acc_match; }
269 let to_global_pos = {
270 let num_parent_commits = segment.num_parent_commits();
271 move |LocalPosition(pos)| IndexPosition(pos + num_parent_commits)
272 };
273 match (acc_match, segment.resolve_change_id_prefix(prefix)) {
275 (NoMatch, local_match) => local_match.map(|(id, positions)| {
276 (id, positions.into_iter().map(to_global_pos).collect())
277 }),
278 (acc_match, NoMatch) => acc_match,
279 (AmbiguousMatch, _) => AmbiguousMatch,
280 (_, AmbiguousMatch) => AmbiguousMatch,
281 (SingleMatch((id1, _)), SingleMatch((id2, _))) if id1 != id2 => AmbiguousMatch,
282 (SingleMatch((id, mut acc_positions)), SingleMatch((_, local_positions))) => {
283 acc_positions
284 .insert_many(0, local_positions.into_iter().map(to_global_pos));
285 SingleMatch((id, acc_positions))
286 }
287 }
288 })
289 }
290
291 pub(super) fn is_ancestor_pos(
292 &self,
293 ancestor_pos: IndexPosition,
294 descendant_pos: IndexPosition,
295 ) -> bool {
296 let ancestor_generation = self.entry_by_pos(ancestor_pos).generation_number();
297 let mut work = vec![descendant_pos];
298 let mut visited = HashSet::new();
299 while let Some(descendant_pos) = work.pop() {
300 let descendant_entry = self.entry_by_pos(descendant_pos);
301 if descendant_pos == ancestor_pos {
302 return true;
303 }
304 if !visited.insert(descendant_entry.position()) {
305 continue;
306 }
307 if descendant_entry.generation_number() <= ancestor_generation {
308 continue;
309 }
310 work.extend(descendant_entry.parent_positions());
311 }
312 false
313 }
314
315 pub(super) fn common_ancestors_pos(
319 &self,
320 set1: Vec<IndexPosition>,
321 set2: Vec<IndexPosition>,
322 ) -> Vec<IndexPosition> {
323 let mut items1 = BinaryHeap::from(set1);
324 let mut items2 = BinaryHeap::from(set2);
325 let mut result = Vec::new();
326 while let (Some(&pos1), Some(&pos2)) = (items1.peek(), items2.peek()) {
327 match pos1.cmp(&pos2) {
328 Ordering::Greater => shift_to_parents(&mut items1, &self.entry_by_pos(pos1)),
329 Ordering::Less => shift_to_parents(&mut items2, &self.entry_by_pos(pos2)),
330 Ordering::Equal => {
331 result.push(pos1);
332 dedup_pop(&mut items1).unwrap();
333 dedup_pop(&mut items2).unwrap();
334 }
335 }
336 }
337 self.heads_pos(result)
338 }
339
340 pub(super) fn all_heads(&self) -> impl Iterator<Item = CommitId> + use<'_> {
341 self.all_heads_pos()
342 .map(move |pos| self.entry_by_pos(pos).commit_id())
343 }
344
345 pub(super) fn all_heads_pos(&self) -> impl Iterator<Item = IndexPosition> + use<> {
346 let num_commits = self.num_commits();
348 let mut not_head: Vec<bool> = vec![false; num_commits as usize];
349 for pos in 0..num_commits {
350 let entry = self.entry_by_pos(IndexPosition(pos));
351 for IndexPosition(parent_pos) in entry.parent_positions() {
352 not_head[parent_pos as usize] = true;
353 }
354 }
355 not_head
356 .into_iter()
357 .enumerate()
358 .filter(|&(_, b)| !b)
359 .map(|(i, _)| IndexPosition(u32::try_from(i).unwrap()))
360 }
361
362 pub fn heads_pos(&self, candidate_positions: Vec<IndexPosition>) -> Vec<IndexPosition> {
369 debug_assert!(candidate_positions
370 .iter()
371 .tuple_windows()
372 .all(|(a, b)| a > b));
373 let Some(min_generation) = candidate_positions
374 .iter()
375 .map(|&pos| self.entry_by_pos(pos).generation_number())
376 .min()
377 else {
378 return candidate_positions;
379 };
380
381 let mut parents = BinaryHeap::new();
385 let mut heads = Vec::new();
386 'outer: for candidate in candidate_positions {
387 while let Some(&parent) = parents.peek().filter(|&&parent| parent >= candidate) {
388 let entry = self.entry_by_pos(parent);
389 if entry.generation_number() <= min_generation {
390 dedup_pop(&mut parents).unwrap();
391 } else {
392 shift_to_parents(&mut parents, &entry);
393 }
394 if parent == candidate {
395 continue 'outer;
397 }
398 }
399 let entry = self.entry_by_pos(candidate);
401 parents.extend(entry.parent_positions());
402 heads.push(candidate);
403 }
404 heads
405 }
406
407 pub fn heads_from_range_and_filter<E>(
411 &self,
412 roots: Vec<IndexPosition>,
413 heads: Vec<IndexPosition>,
414 mut filter: impl FnMut(IndexPosition) -> Result<bool, E>,
415 ) -> Result<Vec<IndexPosition>, E> {
416 if heads.is_empty() {
417 return Ok(heads);
418 }
419 let mut wanted_queue = BinaryHeap::from(heads);
420 let mut unwanted_queue = BinaryHeap::from(roots);
421 let mut found_heads = Vec::new();
422 while let Some(&pos) = wanted_queue.peek() {
423 if shift_to_parents_until(&mut unwanted_queue, self, pos) {
424 dedup_pop(&mut wanted_queue);
425 continue;
426 }
427 let entry = self.entry_by_pos(pos);
428 if filter(pos)? {
429 dedup_pop(&mut wanted_queue);
430 unwanted_queue.extend(entry.parent_positions());
431 found_heads.push(pos);
432 } else {
433 shift_to_parents(&mut wanted_queue, &entry);
434 }
435 }
436 Ok(found_heads)
437 }
438
439 pub(super) fn evaluate_revset(
440 &self,
441 expression: &ResolvedExpression,
442 store: &Arc<Store>,
443 ) -> Result<Box<dyn Revset + '_>, RevsetEvaluationError> {
444 let revset_impl = revset_engine::evaluate(expression, store, self)?;
445 Ok(Box::new(revset_impl))
446 }
447}
448
449impl AsCompositeIndex for CompositeIndex {
450 fn as_composite(&self) -> &CompositeIndex {
451 self
452 }
453}
454
455impl Index for &CompositeIndex {
457 fn shortest_unique_commit_id_prefix_len(&self, commit_id: &CommitId) -> usize {
464 let (prev_id, next_id) = self.resolve_neighbor_commit_ids(commit_id);
465 itertools::chain(prev_id, next_id)
466 .map(|id| hex_util::common_hex_len(commit_id.as_bytes(), id.as_bytes()) + 1)
467 .max()
468 .unwrap_or(0)
469 }
470
471 fn resolve_commit_id_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<CommitId> {
472 self.ancestor_index_segments()
473 .fold(PrefixResolution::NoMatch, |acc_match, segment| {
474 if acc_match == PrefixResolution::AmbiguousMatch {
475 acc_match } else {
477 let local_match = segment.resolve_commit_id_prefix(prefix);
478 acc_match.plus(&local_match)
479 }
480 })
481 }
482
483 fn has_id(&self, commit_id: &CommitId) -> bool {
484 self.commit_id_to_pos(commit_id).is_some()
485 }
486
487 fn is_ancestor(&self, ancestor_id: &CommitId, descendant_id: &CommitId) -> bool {
488 let ancestor_pos = self.commit_id_to_pos(ancestor_id).unwrap();
489 let descendant_pos = self.commit_id_to_pos(descendant_id).unwrap();
490 self.is_ancestor_pos(ancestor_pos, descendant_pos)
491 }
492
493 fn common_ancestors(&self, set1: &[CommitId], set2: &[CommitId]) -> Vec<CommitId> {
494 let pos1 = set1
495 .iter()
496 .map(|id| self.commit_id_to_pos(id).unwrap())
497 .collect_vec();
498 let pos2 = set2
499 .iter()
500 .map(|id| self.commit_id_to_pos(id).unwrap())
501 .collect_vec();
502 self.common_ancestors_pos(pos1, pos2)
503 .iter()
504 .map(|pos| self.entry_by_pos(*pos).commit_id())
505 .collect()
506 }
507
508 fn all_heads_for_gc(
509 &self,
510 ) -> Result<Box<dyn Iterator<Item = CommitId> + '_>, AllHeadsForGcUnsupported> {
511 Ok(Box::new(self.all_heads()))
512 }
513
514 fn heads(
515 &self,
516 candidate_ids: &mut dyn Iterator<Item = &CommitId>,
517 ) -> Result<Vec<CommitId>, IndexError> {
518 let mut candidate_positions = candidate_ids
519 .map(|id| self.commit_id_to_pos(id).unwrap())
520 .collect_vec();
521 candidate_positions.sort_unstable_by_key(|&pos| Reverse(pos));
522 candidate_positions.dedup();
523
524 Ok(self
525 .heads_pos(candidate_positions)
526 .iter()
527 .map(|pos| self.entry_by_pos(*pos).commit_id())
528 .collect())
529 }
530
531 fn evaluate_revset<'index>(
532 &'index self,
533 expression: &ResolvedExpression,
534 store: &Arc<Store>,
535 ) -> Result<Box<dyn Revset + 'index>, RevsetEvaluationError> {
536 CompositeIndex::evaluate_revset(self, expression, store)
537 }
538}
539
540pub(super) struct ChangeIdIndexImpl<I> {
541 index: I,
542 reachable_set: Mutex<AncestorsBitSet>,
543}
544
545impl<I: AsCompositeIndex> ChangeIdIndexImpl<I> {
546 pub fn new(index: I, heads: &mut dyn Iterator<Item = &CommitId>) -> ChangeIdIndexImpl<I> {
547 let composite = index.as_composite();
548 let mut reachable_set = AncestorsBitSet::with_capacity(composite.num_commits());
549 for id in heads {
550 reachable_set.add_head(composite.commit_id_to_pos(id).unwrap());
551 }
552 ChangeIdIndexImpl {
553 index,
554 reachable_set: Mutex::new(reachable_set),
555 }
556 }
557}
558
559impl<I: AsCompositeIndex + Send + Sync> ChangeIdIndex for ChangeIdIndexImpl<I> {
560 fn resolve_prefix(&self, prefix: &HexPrefix) -> PrefixResolution<Vec<CommitId>> {
567 let index = self.index.as_composite();
568 match index.resolve_change_id_prefix(prefix) {
569 PrefixResolution::NoMatch => PrefixResolution::NoMatch,
570 PrefixResolution::SingleMatch((_change_id, positions)) => {
571 debug_assert!(positions.iter().tuple_windows().all(|(a, b)| a < b));
572 let mut reachable_set = self.reachable_set.lock().unwrap();
573 reachable_set.visit_until(index, *positions.first().unwrap());
574 let reachable_commit_ids = positions
575 .iter()
576 .filter(|&&pos| reachable_set.contains(pos))
577 .map(|&pos| index.entry_by_pos(pos).commit_id())
578 .collect_vec();
579 if reachable_commit_ids.is_empty() {
580 PrefixResolution::NoMatch
581 } else {
582 PrefixResolution::SingleMatch(reachable_commit_ids)
583 }
584 }
585 PrefixResolution::AmbiguousMatch => PrefixResolution::AmbiguousMatch,
586 }
587 }
588
589 fn shortest_unique_prefix_len(&self, change_id: &ChangeId) -> usize {
596 self.index
597 .as_composite()
598 .shortest_unique_change_id_prefix_len(change_id)
599 }
600}
601
602pub struct IndexLevelStats {
603 pub num_commits: u32,
604 pub name: Option<String>,
605}
606
607pub struct IndexStats {
608 pub num_commits: u32,
609 pub num_merges: u32,
610 pub max_generation_number: u32,
611 pub num_heads: u32,
612 pub num_changes: u32,
613 pub levels: Vec<IndexLevelStats>,
614}
615
616fn shift_to_parents_until(
619 queue: &mut BinaryHeap<IndexPosition>,
620 index: &CompositeIndex,
621 target_pos: IndexPosition,
622) -> bool {
623 while let Some(&pos) = queue.peek().filter(|&&pos| pos >= target_pos) {
624 shift_to_parents(queue, &index.entry_by_pos(pos));
625 if pos == target_pos {
626 return true;
627 }
628 }
629 false
630}
631
632fn shift_to_parents(items: &mut BinaryHeap<IndexPosition>, entry: &IndexEntry) {
634 let mut parent_positions = entry.parent_positions().into_iter();
635 if let Some(parent_pos) = parent_positions.next() {
636 assert!(parent_pos < entry.position());
637 dedup_replace(items, parent_pos).unwrap();
638 } else {
639 dedup_pop(items).unwrap();
640 return;
641 }
642 for parent_pos in parent_positions {
643 assert!(parent_pos < entry.position());
644 items.push(parent_pos);
645 }
646}
647
648fn dedup_pop<T: Ord>(heap: &mut BinaryHeap<T>) -> Option<T> {
651 let item = heap.pop()?;
652 remove_dup(heap, &item);
653 Some(item)
654}
655
656fn dedup_replace<T: Ord>(heap: &mut BinaryHeap<T>, new_item: T) -> Option<T> {
662 let old_item = {
663 let mut x = heap.peek_mut()?;
664 mem::replace(&mut *x, new_item)
665 };
666 remove_dup(heap, &old_item);
667 Some(old_item)
668}
669
670fn remove_dup<T: Ord>(heap: &mut BinaryHeap<T>, item: &T) {
671 while let Some(x) = heap.peek_mut().filter(|x| **x == *item) {
672 binary_heap::PeekMut::pop(x);
673 }
674}