| //! An implementation of negotiation algorithms to help the server figure out what we have in common so it can optimize | 
 | //! the pack it sends to only contain what we don't have. | 
 | #![deny(rust_2018_idioms, missing_docs)] | 
 | #![forbid(unsafe_code)] | 
 | mod consecutive; | 
 | mod noop; | 
 | mod skipping; | 
 |  | 
 | bitflags::bitflags! { | 
 |     /// Multi purpose, shared flags that are used by negotiation algorithms and by the caller as well | 
 |     /// | 
 |     /// However, in this crate we can't implement the calling side, so we marry this type to whatever is needed in downstream crates. | 
 |     #[derive(Debug, Default, Copy, Clone)] | 
 |     pub struct Flags: u8 { | 
 |         /// The object is already available locally and doesn't need to be fetched by the remote. | 
 |         const COMPLETE = 1 << 0; | 
 |         /// A commit from an alternate object database. | 
 |         const ALTERNATE = 1 << 1; | 
 |  | 
 |         /// The revision is known to be in common with the remote. | 
 |         /// | 
 |         /// Used by `consecutive` and `skipping`. | 
 |         const COMMON = 1 << 2; | 
 |         /// The revision has entered the priority queue. | 
 |         /// | 
 |         /// Used by `consecutive` and `skipping`. | 
 |         const SEEN = 1 << 3; | 
 |         /// The revision was popped off our primary priority queue, used to avoid double-counting of `non_common_revs` | 
 |         /// | 
 |         /// Used by `consecutive` and `skipping`. | 
 |         const POPPED = 1 << 4; | 
 |  | 
 |         /// The revision is common and was set by merit of a remote tracking ref (e.g. `refs/heads/origin/main`). | 
 |         /// | 
 |         /// Used by `consecutive`. | 
 |         const COMMON_REF = 1 << 5; | 
 |  | 
 |         /// The remote let us know it has the object. We still have to tell the server we have this object or one of its descendants. | 
 |         /// We won't tell the server about its ancestors. | 
 |         /// | 
 |         /// Used by `skipping`. | 
 |         const ADVERTISED = 1 << 6; | 
 |     } | 
 | } | 
 |  | 
 | /// Additional data to store with each commit when used by any of our algorithms. | 
 | /// | 
 | /// It's shared among those who use the [`Negotiator`] trait, and all implementations of it. | 
 | #[derive(Default, Debug, Copy, Clone)] | 
 | pub struct Metadata { | 
 |     /// Used by `skipping`. | 
 |     /// Only used if commit is not COMMON | 
 |     pub original_ttl: u16, | 
 |     /// Used by `skipping`. | 
 |     pub ttl: u16, | 
 |     /// Additional information about each commit | 
 |     pub flags: Flags, | 
 | } | 
 |  | 
 | /// The graph our callers use to store traversal information, for (re-)use in the negotiation implementation. | 
 | pub type Graph<'find> = gix_revwalk::Graph<'find, gix_revwalk::graph::Commit<Metadata>>; | 
 |  | 
 | /// A map associating an object id with its commit-metadata. | 
 | pub type IdMap = gix_revwalk::graph::IdMap<gix_revwalk::graph::Commit<Metadata>>; | 
 |  | 
 | /// The way the negotiation is performed. | 
 | #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] | 
 | pub enum Algorithm { | 
 |     /// Do not send any information at all, which typically leads to complete packs to be sent. | 
 |     Noop, | 
 |     /// Walk over consecutive commits and check each one. This can be costly be assures packs are exactly the size they need to be. | 
 |     #[default] | 
 |     Consecutive, | 
 |     /// Like `Consecutive`, but skips commits to converge faster, at the cost of receiving packs that are larger than they have to be. | 
 |     Skipping, | 
 | } | 
 |  | 
 | impl std::fmt::Display for Algorithm { | 
 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
 |         match self { | 
 |             Algorithm::Noop => "noop", | 
 |             Algorithm::Consecutive => "consecutive", | 
 |             Algorithm::Skipping => "skipping", | 
 |         } | 
 |         .fmt(f) | 
 |     } | 
 | } | 
 |  | 
 | /// Calculate how many `HAVE` lines we may send in one round, with variation depending on whether the `transport_is_stateless` or not. | 
 | /// `window_size` is the previous (or initial) value of the window size. | 
 | pub fn window_size(transport_is_stateless: bool, window_size: Option<usize>) -> usize { | 
 |     let current_size = match window_size { | 
 |         None => return 16, | 
 |         Some(cs) => cs, | 
 |     }; | 
 |     const PIPESAFE_FLUSH: usize = 32; | 
 |     const LARGE_FLUSH: usize = 16384; | 
 |  | 
 |     if transport_is_stateless { | 
 |         if current_size < LARGE_FLUSH { | 
 |             current_size * 2 | 
 |         } else { | 
 |             current_size * 11 / 10 | 
 |         } | 
 |     } else if current_size < PIPESAFE_FLUSH { | 
 |         current_size * 2 | 
 |     } else { | 
 |         current_size + PIPESAFE_FLUSH | 
 |     } | 
 | } | 
 |  | 
 | impl Algorithm { | 
 |     /// Create an instance of a negotiator which implements this algorithm. | 
 |     pub fn into_negotiator(self) -> Box<dyn Negotiator> { | 
 |         match &self { | 
 |             Algorithm::Noop => Box::new(noop::Noop) as Box<dyn Negotiator>, | 
 |             Algorithm::Consecutive => Box::<consecutive::Algorithm>::default(), | 
 |             Algorithm::Skipping => Box::<skipping::Algorithm>::default(), | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /// A delegate to implement a negotiation algorithm. | 
 | pub trait Negotiator { | 
 |     /// Mark `id` as common between the remote and us. | 
 |     /// | 
 |     /// These ids are typically the local tips of remote tracking branches. | 
 |     fn known_common(&mut self, id: gix_hash::ObjectId, graph: &mut Graph<'_>) -> Result<(), Error>; | 
 |  | 
 |     /// Add `id` as starting point of a traversal across commits that aren't necessarily common between the remote and us. | 
 |     /// | 
 |     /// These tips are usually the commits of local references whose tips should lead to objects that we have in common with the remote. | 
 |     fn add_tip(&mut self, id: gix_hash::ObjectId, graph: &mut Graph<'_>) -> Result<(), Error>; | 
 |  | 
 |     /// Produce the next id of an object that we want the server to know we have. It's an object we don't know we have in common or not. | 
 |     /// | 
 |     /// Returns `None` if we have exhausted all options, which might mean we have traversed the entire commit graph. | 
 |     fn next_have(&mut self, graph: &mut Graph<'_>) -> Option<Result<gix_hash::ObjectId, Error>>; | 
 |  | 
 |     /// Mark `id` as being common with the remote (as informed by the remote itself) and return `true` if we knew it was common already. | 
 |     /// | 
 |     /// We can assume to have already seen `id` as we were the one to inform the remote in a prior `have`. | 
 |     fn in_common_with_remote(&mut self, id: gix_hash::ObjectId, graph: &mut Graph<'_>) -> Result<bool, Error>; | 
 | } | 
 |  | 
 | /// An error that happened during any of the methods on a [`Negotiator`]. | 
 | pub type Error = gix_revwalk::graph::try_lookup_or_insert_default::Error; |