From c41904b765991cb02198591ac31fe6922fd21669 Mon Sep 17 00:00:00 2001 From: Jun Wu Date: Tue, 20 Oct 2020 15:15:45 -0700 Subject: [PATCH] dag: impl DagAlgorithm for AbstractNameDag Summary: I copied the code since it's hard to implement using the macros. In the future I plan to merge MemNameDag into AbstractNameDag and remove the macros. Reviewed By: sfilipco Differential Revision: D24399517 fbshipit-source-id: 326e76cd06a6e1ad26b39bcb51ba0ff24106c984 --- eden/scm/lib/dag/src/namedag.rs | 245 +++++++++++++++++++++++++++++++- 1 file changed, 243 insertions(+), 2 deletions(-) diff --git a/eden/scm/lib/dag/src/namedag.rs b/eden/scm/lib/dag/src/namedag.rs index 91396f7fd5..d4e440f3a5 100644 --- a/eden/scm/lib/dag/src/namedag.rs +++ b/eden/scm/lib/dag/src/namedag.rs @@ -308,6 +308,14 @@ where } } } + + fn dag(&self) -> &IdDag { + &self.dag + } + + fn map(&self) -> &M { + &self.map + } } impl TryClone for NameDagState { @@ -618,7 +626,234 @@ macro_rules! impl_dag_algorithms { }; } -impl_dag_algorithms!(NameDag); +/// DAG related read-only algorithms. +impl DagAlgorithm for AbstractNameDag, M, S> +where + IS: IdDagStore, + IdDag: TryClone + Send + Sync + 'static, + M: TryClone + IdMapAssignHead + Sync + Send + 'static, + S: TryClone + Sync + Send + 'static, +{ + /// Sort a `NameSet` topologically. + fn sort(&self, set: &NameSet) -> Result { + if set.hints().contains(Flags::TOPO_DESC) + && set.hints().is_dag_compatible(self.dag_snapshot()?) + { + Ok(set.clone()) + } else { + let flags = extract_ancestor_flag_if_compatible(set.hints(), self.dag_snapshot()?); + let mut spans = SpanSet::empty(); + for name in set.iter()? { + let id = self.map().vertex_id(name?)?; + spans.push(id); + } + let result = NameSet::from_spans_dag(spans, self)?; + result.hints().add_flags(flags); + Ok(result) + } + } + + /// Get ordered parent vertexes. + fn parent_names(&self, name: VertexName) -> Result> { + let id = self.map().vertex_id(name)?; + self.dag() + .parent_ids(id)? + .into_iter() + .map(|id| self.map().vertex_name(id)) + .collect() + } + + /// Returns a [`SpanSet`] that covers all vertexes tracked by this DAG. + fn all(&self) -> Result { + let spans = self.dag().all()?; + let result = NameSet::from_spans_dag(spans, self)?; + result.hints().add_flags(Flags::FULL); + Ok(result) + } + + /// Calculates all ancestors reachable from any name from the given set. + fn ancestors(&self, set: NameSet) -> Result { + if set.hints().contains(Flags::ANCESTORS) + && set.hints().is_dag_compatible(self.dag_snapshot()?) + { + return Ok(set); + } + let spans = self.to_id_set(&set)?; + let spans = self.dag().ancestors(spans)?; + let result = NameSet::from_spans_dag(spans, self)?; + result.hints().add_flags(Flags::ANCESTORS); + Ok(result) + } + + /// Calculates parents of the given set. + /// + /// Note: Parent order is not preserved. Use [`NameDag::parent_names`] + /// to preserve order. + fn parents(&self, set: NameSet) -> Result { + // Preserve ANCESTORS flag. If ancestors(x) == x, then ancestors(parents(x)) == parents(x). + let flags = extract_ancestor_flag_if_compatible(set.hints(), self.dag_snapshot()?); + let spans = self.dag().parents(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + result.hints().add_flags(flags); + #[cfg(test)] + { + result.assert_eq(crate::default_impl::parents(self, set)?); + } + Ok(result) + } + + /// Calculates the n-th first ancestor. + fn first_ancestor_nth(&self, name: VertexName, n: u64) -> Result { + #[cfg(test)] + let name2 = name.clone(); + let id = self.map().vertex_id(name)?; + let id = self.dag().first_ancestor_nth(id, n)?; + let result = self.map().vertex_name(id)?; + #[cfg(test)] + { + let result2 = crate::default_impl::first_ancestor_nth(self, name2, n)?; + assert_eq!(result, result2); + } + Ok(result) + } + + /// Calculates heads of the given set. + fn heads(&self, set: NameSet) -> Result { + if set.hints().contains(Flags::ANCESTORS) + && set.hints().is_dag_compatible(self.dag_snapshot()?) + { + // heads_ancestors is faster. + return self.heads_ancestors(set); + } + let spans = self.dag().heads(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + #[cfg(test)] + { + result.assert_eq(crate::default_impl::heads(self, set)?); + } + Ok(result) + } + + /// Calculates children of the given set. + fn children(&self, set: NameSet) -> Result { + let spans = self.dag().children(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + Ok(result) + } + + /// Calculates roots of the given set. + fn roots(&self, set: NameSet) -> Result { + let flags = extract_ancestor_flag_if_compatible(set.hints(), self.dag_snapshot()?); + let spans = self.dag().roots(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + result.hints().add_flags(flags); + #[cfg(test)] + { + result.assert_eq(crate::default_impl::roots(self, set)?); + } + Ok(result) + } + + /// Calculates one "greatest common ancestor" of the given set. + /// + /// If there are no common ancestors, return None. + /// If there are multiple greatest common ancestors, pick one arbitrarily. + /// Use `gca_all` to get all of them. + fn gca_one(&self, set: NameSet) -> Result> { + let result: Option = match self.dag().gca_one(self.to_id_set(&set)?)? { + None => None, + Some(id) => Some(self.map().vertex_name(id)?), + }; + #[cfg(test)] + { + assert_eq!(&result, &crate::default_impl::gca_one(self, set)?); + } + Ok(result) + } + + /// Calculates all "greatest common ancestor"s of the given set. + /// `gca_one` is faster if an arbitrary answer is ok. + fn gca_all(&self, set: NameSet) -> Result { + let spans = self.dag().gca_all(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + #[cfg(test)] + { + result.assert_eq(crate::default_impl::gca_all(self, set)?); + } + Ok(result) + } + + /// Calculates all common ancestors of the given set. + fn common_ancestors(&self, set: NameSet) -> Result { + let spans = self.dag().common_ancestors(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + result.hints().add_flags(Flags::ANCESTORS); + #[cfg(test)] + { + result.assert_eq(crate::default_impl::common_ancestors(self, set)?); + } + Ok(result) + } + + /// Tests if `ancestor` is an ancestor of `descendant`. + fn is_ancestor(&self, ancestor: VertexName, descendant: VertexName) -> Result { + #[cfg(test)] + let result2 = crate::default_impl::is_ancestor(self, ancestor.clone(), descendant.clone())?; + let ancestor_id = self.map().vertex_id(ancestor)?; + let descendant_id = self.map().vertex_id(descendant)?; + let result = self.dag().is_ancestor(ancestor_id, descendant_id)?; + #[cfg(test)] + { + assert_eq!(&result, &result2); + } + Ok(result) + } + + /// Calculates "heads" of the ancestors of the given set. That is, + /// Find Y, which is the smallest subset of set X, where `ancestors(Y)` is + /// `ancestors(X)`. + /// + /// This is faster than calculating `heads(ancestors(set))`. + /// + /// This is different from `heads`. In case set contains X and Y, and Y is + /// an ancestor of X, but not the immediate ancestor, `heads` will include + /// Y while this function won't. + fn heads_ancestors(&self, set: NameSet) -> Result { + let spans = self.dag().heads_ancestors(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + #[cfg(test)] + { + // default_impl::heads_ancestors calls `heads` if `Flags::ANCESTORS` + // is set. Prevent infinite loop. + if !set.hints().contains(Flags::ANCESTORS) { + result.assert_eq(crate::default_impl::heads_ancestors(self, set)?); + } + } + Ok(result) + } + + /// Calculates the "dag range" - vertexes reachable from both sides. + fn range(&self, roots: NameSet, heads: NameSet) -> Result { + let roots = self.to_id_set(&roots)?; + let heads = self.to_id_set(&heads)?; + let spans = self.dag().range(roots, heads)?; + let result = NameSet::from_spans_dag(spans, self)?; + Ok(result) + } + + /// Calculates the descendants of the given set. + fn descendants(&self, set: NameSet) -> Result { + let spans = self.dag().descendants(self.to_id_set(&set)?)?; + let result = NameSet::from_spans_dag(spans, self)?; + Ok(result) + } + + /// Get a snapshot of the current graph. + fn dag_snapshot(&self) -> Result> { + Ok(self.try_snapshot()? as Arc) + } +} + impl_dag_algorithms!(MemNameDag); /// Extract the ANCESTORS flag if the set with the `hints` is bound to a @@ -803,7 +1038,13 @@ impl NameDagStorage for MemNameDag { } } -impl IdMapSnapshot for NameDag { +impl IdMapSnapshot for AbstractNameDag, M, S> +where + IS: IdDagStore, + IdDag: TryClone + Send + Sync + 'static, + M: TryClone + IdMapAssignHead + Send + Sync + 'static, + S: TryClone + Send + Sync + 'static, +{ fn id_map_snapshot(&self) -> Result> { Ok(self.try_snapshot()? as Arc) }