jj_lib/default_index/
composite.rs

1// Copyright 2023 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#![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    /// Suppose the given `commit_id` exists, returns the previous and next
67    /// commit ids in lexicographical order.
68    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
98/// Abstraction over owned and borrowed types that can be cheaply converted to
99/// a `CompositeIndex` reference.
100pub trait AsCompositeIndex {
101    /// Returns reference wrapper that provides global access to this index.
102    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/// `CompositeIndex` provides an index of both commit IDs and change IDs.
118///
119/// We refer to this as a composite index because it's a composite of multiple
120/// nested index segments where each parent segment is roughly twice as large
121/// its child. segment. This provides a good balance between read and write
122/// performance.
123// Reference wrapper that provides global access to nested index segments.
124#[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    /// Iterates parent and its ancestor readonly index segments.
133    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    /// Iterates self and its ancestor index segments.
141    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    /// Suppose the given `commit_id` exists, returns the previous and next
211    /// commit ids in lexicographical order.
212    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    /// Suppose the given `change_id` exists, returns the minimum prefix length
228    /// to disambiguate it within all the indexed ids including hidden ones.
229    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    /// Suppose the given `change_id` exists, returns the previous and next
238    /// change ids in lexicographical order. The returned change ids may be
239    /// hidden.
240    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    /// Resolves the given change id `prefix` to the associated entries. The
256    /// returned entries may be hidden.
257    ///
258    /// The returned index positions are sorted in ascending order.
259    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; // avoid checking the parent file(s)
268                }
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                // Similar to PrefixResolution::plus(), but merges matches of the same id.
274                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    /// Computes the greatest common ancestors.
316    ///
317    /// The returned index positions are sorted in descending order.
318    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        // TODO: can be optimized to use bit vec and leading/trailing_ones()
347        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    /// Returns the subset of positions in `candidate_positions` which refer to
363    /// entries that are heads in the repository.
364    ///
365    /// The `candidate_positions` must be sorted in descending order, and have
366    /// no duplicates. The returned head positions are also sorted in descending
367    /// order.
368    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        // Iterate though the candidates by reverse index position, keeping track of the
382        // ancestors of already-found heads. If a candidate is an ancestor of an
383        // already-found head, then it can be removed.
384        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                    // The candidate is an ancestor of an existing head, so we can skip it.
396                    continue 'outer;
397                }
398            }
399            // No parents matched, so this commit is a head.
400            let entry = self.entry_by_pos(candidate);
401            parents.extend(entry.parent_positions());
402            heads.push(candidate);
403        }
404        heads
405    }
406
407    /// Find the heads of a range of positions `roots..heads`, applying a filter
408    /// to the commits in the range. The heads are sorted in descending order.
409    /// The filter will also be called in descending index position order.
410    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
455// In revset engine, we need to convert &CompositeIndex to &dyn Index.
456impl Index for &CompositeIndex {
457    /// Suppose the given `commit_id` exists, returns the minimum prefix length
458    /// to disambiguate it. The length to be returned is a number of hexadecimal
459    /// digits.
460    ///
461    /// If the given `commit_id` doesn't exist, this will return the prefix
462    /// length that never matches with any commit ids.
463    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 // avoid checking the parent file(s)
476                } 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    // Resolves change id prefix among all ids, then filters out hidden
561    // entries.
562    //
563    // If `SingleMatch` is returned, the commits including in the set are all
564    // visible. `AmbiguousMatch` may be returned even if the prefix is unique
565    // within the visible entries.
566    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    // Calculates the shortest prefix length of the given `change_id` among all
590    // IDs, including hidden entries.
591    //
592    // The returned length is usually a few digits longer than the minimum
593    // length necessary to disambiguate within the visible entries since hidden
594    // entries are also considered when determining the prefix length.
595    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
616/// Repeatedly `shift_to_parents` until reaching a target position. Returns true
617/// if the target position matched a position in the queue.
618fn 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
632/// Removes an entry from the queue and replace it with its parents.
633fn 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
648/// Removes the greatest items (including duplicates) from the heap, returns
649/// one.
650fn 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
656/// Removes the greatest items (including duplicates) from the heap, inserts
657/// lesser `new_item` to the heap, returns the removed one.
658///
659/// This is faster than calling `dedup_pop(heap)` and `heap.push(new_item)`
660/// especially when `new_item` is the next greatest item.
661fn 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}