dag: move algorithms from IdDag<Store> to Store

Summary:
Previously, algorithms are defined on `IdDag<Store>`, which requires a type
parameter to use. Move them to be defined directly on `Store` so using the
algorithms no longer require a type parameter. This will make it easier
to write code that calls algorithms on different IdDagStores.

Reviewed By: sfilipco

Differential Revision: D26360561

fbshipit-source-id: 8e0faf741019c4ed4119ad8e754aea9057b31866
This commit is contained in:
Jun Wu 2021-02-10 12:26:22 -08:00 committed by Facebook GitHub Bot
parent e270d50f75
commit aa9dfeff2e
5 changed files with 67 additions and 59 deletions

View File

@ -208,7 +208,7 @@ fn bench_with_iddag<S: IdDagStore + Persist>(get_empty_iddag: impl Fn() -> IdDag
elapsed(|| {
for set in &sample_two_ids {
let ids: Vec<_> = set.iter().collect();
dag.range(ids[0], ids[1]).unwrap();
dag.range(ids[0].into(), ids[1].into()).unwrap();
}
})
});

View File

@ -60,7 +60,7 @@ fn main() {
elapsed(|| {
for i in (0..parents.len() as u64).step_by(10079) {
for j in (1..parents.len() as u64).step_by(2351) {
dag.gca_one((Id(i), Id(j))).unwrap();
dag.gca_one((Id(i), Id(j)).into()).unwrap();
}
}
})

View File

@ -28,6 +28,7 @@ use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::{BTreeSet, BinaryHeap};
use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
#[cfg(any(test, feature = "indexedlog-backend"))]
use std::path::Path;
use tracing::{debug_span, field, trace};
@ -332,7 +333,7 @@ impl<Store: IdDagStore> IdDag<Store> {
let mut current_parents = Vec::new();
let mut insert_count = 0;
let mut head_ids: HashSet<Id> = if group == Group::MASTER && low > Id::MIN {
self.heads(Id::MIN..=(low - 1))?.iter().collect()
self.heads((Id::MIN..=(low - 1)).into())?.iter().collect()
} else {
Default::default()
};
@ -557,9 +558,9 @@ impl<Store: IdDagStore> IdDag<Store> {
}
// User-facing DAG-related algorithms.
impl<Store: IdDagStore> IdDag<Store> {
pub trait IdDagAlgorithm: IdDagStore {
/// Return a [`IdSet`] that covers all ids stored in this [`IdDag`].
pub fn all(&self) -> Result<IdSet> {
fn all(&self) -> Result<IdSet> {
let mut result = IdSet::empty();
for &group in Group::ALL.iter().rev() {
let next = self.next_free_id(0, group)?;
@ -571,7 +572,7 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Return a [`IdSet`] that covers all ids stored in the master group.
pub(crate) fn master_group(&self) -> Result<IdSet> {
fn master_group(&self) -> Result<IdSet> {
let group = Group::MASTER;
let next = self.next_free_id(0, group)?;
if next > group.min_id() {
@ -586,8 +587,7 @@ impl<Store: IdDagStore> IdDag<Store> {
/// ```plain,ignore
/// union(ancestors(i) for i in set)
/// ```
pub fn ancestors(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let mut set: IdSet = set.into();
fn ancestors(&self, mut set: IdSet) -> Result<IdSet> {
let tracing_span = debug_span!("ancestors", result = "", set = field::debug(&set));
let _scope = tracing_span.enter();
if set.count() > 2 {
@ -597,6 +597,7 @@ impl<Store: IdDagStore> IdDag<Store> {
}
let mut result = IdSet::empty();
let mut to_visit: BinaryHeap<_> = set.iter().collect();
let max_level = self.max_level()?;
'outer: while let Some(id) = to_visit.pop() {
if result.contains(id) {
// If `id` is in `result`, then `ancestors(id)` are all in `result`.
@ -612,7 +613,7 @@ impl<Store: IdDagStore> IdDag<Store> {
break 'outer;
}
}
for level in (1..=self.max_level).rev() {
for level in (1..=max_level).rev() {
let seg = self.find_segment_by_head_and_level(id, level)?;
if let Some(seg) = seg {
let span = seg.span()?.into();
@ -648,8 +649,7 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Like `ancestors` but follows only the first parents.
pub fn first_ancestors(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set: IdSet = set.into();
fn first_ancestors(&self, set: IdSet) -> Result<IdSet> {
let tracing_span = debug_span!("first_ancestors", result = "", set = field::debug(&set));
let _scope = tracing_span.enter();
let mut result = IdSet::empty();
@ -677,9 +677,8 @@ impl<Store: IdDagStore> IdDag<Store> {
/// Calculate merges within the given set.
pub fn merges(&self, set: impl Into<IdSet>) -> Result<IdSet> {
fn merges(&self, set: IdSet) -> Result<IdSet> {
let mut result = IdSet::empty();
let set = set.into();
let tracing_span = debug_span!("merges", result = "", set = field::debug(&set));
let _scope = tracing_span.enter();
@ -741,17 +740,17 @@ impl<Store: IdDagStore> IdDag<Store> {
///
/// Note: [`IdSet`] does not preserve order. Use [`IdDag::parent_ids`] if
/// order is needed.
pub fn parents(&self, set: impl Into<IdSet>) -> Result<IdSet> {
fn parents(&self, mut set: IdSet) -> Result<IdSet> {
let mut result = IdSet::empty();
let mut set = set.into();
let tracing_span = debug_span!("parents", result = "", set = field::debug(&set));
let _scope = tracing_span.enter();
let max_level = self.max_level()?;
'outer: while let Some(head) = set.max() {
// For high-level segments. If the set covers the entire segment, then
// the parents is (the segment - its head + its parents).
for level in (1..=self.max_level).rev() {
for level in (1..=max_level).rev() {
if let Some(seg) = self.find_segment_by_head_and_level(head, level)? {
let seg_span = seg.span()?;
if set.contains(seg_span) {
@ -804,7 +803,7 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Get parents of a single `id`. Preserve the order.
pub fn parent_ids(&self, id: Id) -> Result<Vec<Id>> {
fn parent_ids(&self, id: Id) -> Result<Vec<Id>> {
let seg = match self.find_flat_segment_including_id(id)? {
Some(seg) => seg,
None => return id.not_found(),
@ -819,7 +818,7 @@ impl<Store: IdDagStore> IdDag<Store> {
/// Calculate the n-th first ancestor. If `n` is 0, return `id` unchanged.
/// If `n` is 1, return the first parent of `id`.
pub fn first_ancestor_nth(&self, id: Id, n: u64) -> Result<Id> {
fn first_ancestor_nth(&self, id: Id, n: u64) -> Result<Id> {
match self.try_first_ancestor_nth(id, n)? {
None => Err(Programming(format!(
"{}~{} cannot be resolved - no parents",
@ -833,7 +832,7 @@ impl<Store: IdDagStore> IdDag<Store> {
/// If `n` is 1, return the first parent of `id`.
/// If `n` is too large, exceeding the distance between the root and `id`,
/// return `None`.
pub fn try_first_ancestor_nth(&self, mut id: Id, mut n: u64) -> Result<Option<Id>> {
fn try_first_ancestor_nth(&self, mut id: Id, mut n: u64) -> Result<Option<Id>> {
// PERF: this can have fast paths from high-level segments if high-level
// segments have extra information.
while n > 0 {
@ -863,7 +862,7 @@ impl<Store: IdDagStore> IdDag<Store> {
/// Convert an `id` to `x~n` form with the given constraint.
///
/// Return `None` if the conversion can not be done with the constraints.
pub fn to_first_ancestor_nth(
fn to_first_ancestor_nth(
&self,
id: Id,
constraint: FirstAncestorConstraint,
@ -930,19 +929,18 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Calculate heads of the given set.
pub fn heads(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set = set.into();
fn heads(&self, set: IdSet) -> Result<IdSet> {
Ok(set.difference(&self.parents(set.clone())?))
}
/// Calculate children for a single `Id`.
pub fn children_id(&self, id: Id) -> Result<IdSet> {
fn children_id(&self, id: Id) -> Result<IdSet> {
let mut result = BTreeSet::new();
for seg in self.store.iter_flat_segments_with_parent(id)? {
for seg in self.iter_flat_segments_with_parent(id)? {
let seg = seg?;
result.insert(seg.span()?.low);
}
if let Some(seg) = self.store.find_flat_segment_including_id(id)? {
if let Some(seg) = self.find_flat_segment_including_id(id)? {
let span = seg.span()?;
if span.high != id {
result.insert(id + 1);
@ -953,8 +951,7 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Calculate children of the given set.
pub fn children(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set = set.into();
fn children(&self, set: IdSet) -> Result<IdSet> {
if set.count() < 5 {
let result =
set.iter()
@ -991,14 +988,14 @@ impl<Store: IdDagStore> IdDag<Store> {
// - Yes: Figure out children in the flat segment.
// Push them to the result.
struct Context<'a, Store> {
this: &'a IdDag<Store>,
struct Context<'a, Store: ?Sized> {
this: &'a Store,
set: IdSet,
result_lower_bound: Id,
result: IdSet,
}
fn visit_segments<S: IdDagStore>(
fn visit_segments<S: IdDagStore + ?Sized>(
ctx: &mut Context<S>,
mut range: IdSpan,
level: Level,
@ -1096,8 +1093,9 @@ impl<Store: IdDagStore> IdDag<Store> {
result: IdSet::empty(),
};
let max_level = self.max_level()?;
for span in self.all()?.as_spans() {
visit_segments(&mut ctx, *span, self.max_level)?;
visit_segments(&mut ctx, *span, max_level)?;
}
if !tracing_span.is_disabled() {
@ -1108,8 +1106,7 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Calculate roots of the given set.
pub fn roots(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set = set.into();
fn roots(&self, set: IdSet) -> Result<IdSet> {
Ok(set.difference(&self.children(set.clone())?))
}
@ -1118,16 +1115,14 @@ impl<Store: IdDagStore> IdDag<Store> {
/// 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.
pub fn gca_one(&self, set: impl Into<IdSet>) -> Result<Option<Id>> {
let set = set.into();
fn gca_one(&self, set: IdSet) -> Result<Option<Id>> {
// The set is sorted in DESC order. Therefore its first item can be used as the result.
Ok(self.common_ancestors(set)?.max())
}
/// Calculate all "greatest common ancestor"s of the given set.
/// `gca_one` is faster if an arbitrary answer is ok.
pub fn gca_all(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set = set.into();
fn gca_all(&self, set: IdSet) -> Result<IdSet> {
self.heads_ancestors(self.common_ancestors(set)?)
}
@ -1136,8 +1131,7 @@ impl<Store: IdDagStore> IdDag<Store> {
/// ```plain,ignore
/// intersect(ancestors(i) for i in set)
/// ```
pub fn common_ancestors(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set = set.into();
fn common_ancestors(&self, set: IdSet) -> Result<IdSet> {
let result = match set.count() {
0 => set,
1 => self.ancestors(set)?,
@ -1146,7 +1140,8 @@ impl<Store: IdDagStore> IdDag<Store> {
let mut iter = set.iter();
let a = iter.next().unwrap();
let b = iter.next().unwrap();
self.ancestors(a)?.intersection(&self.ancestors(b)?)
self.ancestors(a.into())?
.intersection(&self.ancestors(b.into())?)
}
_ => {
// Try to reduce the size of `set`.
@ -1154,7 +1149,7 @@ impl<Store: IdDagStore> IdDag<Store> {
let set = self.roots(set)?;
set.iter()
.fold(Ok(IdSet::full()), |set: Result<IdSet>, id| {
Ok(set?.intersection(&self.ancestors(id)?))
Ok(set?.intersection(&self.ancestors(id.into())?))
})?
}
};
@ -1162,8 +1157,8 @@ impl<Store: IdDagStore> IdDag<Store> {
}
/// Test if `ancestor_id` is an ancestor of `descendant_id`.
pub fn is_ancestor(&self, ancestor_id: Id, descendant_id: Id) -> Result<bool> {
let set = self.ancestors(descendant_id)?;
fn is_ancestor(&self, ancestor_id: Id, descendant_id: Id) -> Result<bool> {
let set = self.ancestors(descendant_id.into())?;
Ok(set.contains(ancestor_id))
}
@ -1176,14 +1171,13 @@ impl<Store: IdDagStore> IdDag<Store> {
/// 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.
pub fn heads_ancestors(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let set = set.into();
fn heads_ancestors(&self, set: IdSet) -> Result<IdSet> {
let mut remaining = set;
let mut result = IdSet::empty();
while let Some(id) = remaining.max() {
result.push_span((id..=id).into());
// Remove ancestors reachable from that head.
remaining = remaining.difference(&self.ancestors(id)?);
remaining = remaining.difference(&self.ancestors(id.into())?);
}
Ok(result)
}
@ -1195,12 +1189,10 @@ impl<Store: IdDagStore> IdDag<Store> {
/// ```
///
/// This is O(flat segments), or O(merges).
pub fn range(&self, roots: impl Into<IdSet>, heads: impl Into<IdSet>) -> Result<IdSet> {
let roots = roots.into();
fn range(&self, roots: IdSet, mut heads: IdSet) -> Result<IdSet> {
if roots.is_empty() {
return Ok(IdSet::empty());
}
let mut heads = heads.into();
if heads.is_empty() {
return Ok(IdSet::empty());
}
@ -1229,8 +1221,8 @@ impl<Store: IdDagStore> IdDag<Store> {
/// Logically equivalent to `range(set, all())`.
///
/// This is O(flat segments), or O(merges).
pub fn descendants(&self, set: impl Into<IdSet>) -> Result<IdSet> {
let roots = set.into();
fn descendants(&self, set: IdSet) -> Result<IdSet> {
let roots = set;
let result = self.descendants_intersection(&roots, &self.all()?)?;
Ok(result)
}
@ -1358,6 +1350,16 @@ impl<Store: IdDagStore> IdDag<Store> {
}
}
impl<S: IdDagStore> IdDagAlgorithm for S {}
impl<Store: IdDagStore> Deref for IdDag<Store> {
type Target = dyn IdDagAlgorithm;
fn deref(&self) -> &Self::Target {
&self.store
}
}
// Full IdMap -> Sparse IdMap
impl<Store: IdDagStore> IdDag<Store> {
/// Copy a subset of "Universal" mapping from `full_idmap` to
@ -1626,7 +1628,10 @@ mod tests {
assert_eq!(dag.max_level, 3);
assert_eq!(
dag.children(Id(1000)).unwrap().iter().collect::<Vec<Id>>(),
dag.children(Id(1000).into())
.unwrap()
.iter()
.collect::<Vec<Id>>(),
vec![Id(1001)]
);
}

View File

@ -21,7 +21,7 @@ pub(crate) use in_process_store::InProcessStore;
#[cfg(any(test, feature = "indexedlog-backend"))]
pub(crate) use indexedlog_store::IndexedLogStore;
pub trait IdDagStore: Send + Sync {
pub trait IdDagStore: Send + Sync + 'static {
/// Maximum level segment in the store
fn max_level(&self) -> Result<Level>;

View File

@ -829,7 +829,7 @@ fn test_segment_ancestors_example1() {
(1, 2),
(0, 1),
] {
assert_eq!(dag.ancestors(id).unwrap().count(), count);
assert_eq!(dag.ancestors(id.into()).unwrap().count(), count);
}
for (a, b, ancestor) in vec![
@ -845,9 +845,9 @@ fn test_segment_ancestors_example1() {
let ancestor = ancestor.map(Id);
let a = Id(a);
let b = Id(b);
assert_eq!(dag.gca_one((a, b)).unwrap(), ancestor);
assert_eq!(dag.gca_all((a, b)).unwrap().iter().nth(0), ancestor);
assert_eq!(dag.gca_all((a, b)).unwrap().iter().nth(1), None);
assert_eq!(dag.gca_one((a, b).into()).unwrap(), ancestor);
assert_eq!(dag.gca_all((a, b).into()).unwrap().iter().nth(0), ancestor);
assert_eq!(dag.gca_all((a, b).into()).unwrap().iter().nth(1), None);
assert_eq!(dag.is_ancestor(b, a).unwrap(), ancestor == Some(b));
assert_eq!(dag.is_ancestor(a, b).unwrap(), ancestor == Some(a));
}
@ -885,9 +885,12 @@ Lv1: R0-2[]"#
);
let dag = result.name_dag.dag;
// This is kind of "undefined" whether it's 1 or 0.
assert_eq!(dag.gca_one((2, 3)).unwrap(), Some(Id(1)));
assert_eq!(dag.gca_one((2, 3).into()).unwrap(), Some(Id(1)));
assert_eq!(
dag.gca_all((2, 3)).unwrap().iter().collect::<Vec<_>>(),
dag.gca_all((2, 3).into())
.unwrap()
.iter()
.collect::<Vec<_>>(),
vec![1, 0]
);
}