dag: add ToIdSet trait

Summary: The trait converts NameSet to IdSet. It'll be used by the revlog index.

Reviewed By: sfilipco

Differential Revision: D21795869

fbshipit-source-id: 55f7a238158442db9d8bdfe84e64438be504f618
This commit is contained in:
Jun Wu 2020-06-03 13:20:11 -07:00 committed by Facebook GitHub Bot
parent 45d6b00593
commit 234147239a
2 changed files with 68 additions and 46 deletions

View File

@ -31,7 +31,9 @@ use crate::ops::DagAddHeads;
use crate::ops::DagAlgorithm; use crate::ops::DagAlgorithm;
use crate::ops::DagPersistent; use crate::ops::DagPersistent;
use crate::ops::IdConvert; use crate::ops::IdConvert;
use crate::ops::IdMapEq;
use crate::ops::PrefixLookup; use crate::ops::PrefixLookup;
use crate::ops::ToIdSet;
use crate::spanset::SpanSet; use crate::spanset::SpanSet;
use anyhow::{anyhow, bail, ensure, Result}; use anyhow::{anyhow, bail, ensure, Result};
use indexedlog::multi; use indexedlog::multi;
@ -356,7 +358,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Calculates all ancestors reachable from any name from the given set. /// Calculates all ancestors reachable from any name from the given set.
fn ancestors(&self, set: NameSet) -> Result<NameSet> { fn ancestors(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().ancestors(self.to_span_set(&set)?)?; let spans = self.dag().ancestors(self.to_id_set(&set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.clone_map())) Ok(NameSet::from_spans_idmap(spans, self.clone_map()))
} }
@ -365,7 +367,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Note: Parent order is not preserved. Use [`NameDag::parent_names`] /// Note: Parent order is not preserved. Use [`NameDag::parent_names`]
/// to preserve order. /// to preserve order.
fn parents(&self, set: NameSet) -> Result<NameSet> { fn parents(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().parents(self.to_span_set(&set)?)?; let spans = self.dag().parents(self.to_id_set(&set)?)?;
let result = NameSet::from_spans_idmap(spans, self.clone_map()); let result = NameSet::from_spans_idmap(spans, self.clone_map());
#[cfg(test)] #[cfg(test)]
{ {
@ -391,7 +393,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Calculates heads of the given set. /// Calculates heads of the given set.
fn heads(&self, set: NameSet) -> Result<NameSet> { fn heads(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().heads(self.to_span_set(&set)?)?; let spans = self.dag().heads(self.to_id_set(&set)?)?;
let result = NameSet::from_spans_idmap(spans, self.clone_map()); let result = NameSet::from_spans_idmap(spans, self.clone_map());
#[cfg(test)] #[cfg(test)]
{ {
@ -402,13 +404,13 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Calculates children of the given set. /// Calculates children of the given set.
fn children(&self, set: NameSet) -> Result<NameSet> { fn children(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().children(self.to_span_set(&set)?)?; let spans = self.dag().children(self.to_id_set(&set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.clone_map())) Ok(NameSet::from_spans_idmap(spans, self.clone_map()))
} }
/// Calculates roots of the given set. /// Calculates roots of the given set.
fn roots(&self, set: NameSet) -> Result<NameSet> { fn roots(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().roots(self.to_span_set(&set)?)?; let spans = self.dag().roots(self.to_id_set(&set)?)?;
let result = NameSet::from_spans_idmap(spans, self.clone_map()); let result = NameSet::from_spans_idmap(spans, self.clone_map());
#[cfg(test)] #[cfg(test)]
{ {
@ -423,7 +425,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// If there are multiple greatest common ancestors, pick one arbitrarily. /// If there are multiple greatest common ancestors, pick one arbitrarily.
/// Use `gca_all` to get all of them. /// Use `gca_all` to get all of them.
fn gca_one(&self, set: NameSet) -> Result<Option<VertexName>> { fn gca_one(&self, set: NameSet) -> Result<Option<VertexName>> {
let result: Option<VertexName> = match self.dag().gca_one(self.to_span_set(&set)?)? { let result: Option<VertexName> = match self.dag().gca_one(self.to_id_set(&set)?)? {
None => None, None => None,
Some(id) => Some(self.map().vertex_name(id)?), Some(id) => Some(self.map().vertex_name(id)?),
}; };
@ -437,7 +439,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Calculates all "greatest common ancestor"s of the given set. /// Calculates all "greatest common ancestor"s of the given set.
/// `gca_one` is faster if an arbitrary answer is ok. /// `gca_one` is faster if an arbitrary answer is ok.
fn gca_all(&self, set: NameSet) -> Result<NameSet> { fn gca_all(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().gca_all(self.to_span_set(&set)?)?; let spans = self.dag().gca_all(self.to_id_set(&set)?)?;
let result = NameSet::from_spans_idmap(spans, self.clone_map()); let result = NameSet::from_spans_idmap(spans, self.clone_map());
#[cfg(test)] #[cfg(test)]
{ {
@ -448,7 +450,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Calculates all common ancestors of the given set. /// Calculates all common ancestors of the given set.
fn common_ancestors(&self, set: NameSet) -> Result<NameSet> { fn common_ancestors(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().common_ancestors(self.to_span_set(&set)?)?; let spans = self.dag().common_ancestors(self.to_id_set(&set)?)?;
let result = NameSet::from_spans_idmap(spans, self.clone_map()); let result = NameSet::from_spans_idmap(spans, self.clone_map());
#[cfg(test)] #[cfg(test)]
{ {
@ -481,7 +483,7 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// an ancestor of X, but not the immediate ancestor, `heads` will include /// an ancestor of X, but not the immediate ancestor, `heads` will include
/// Y while this function won't. /// Y while this function won't.
fn heads_ancestors(&self, set: NameSet) -> Result<NameSet> { fn heads_ancestors(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().heads_ancestors(self.to_span_set(&set)?)?; let spans = self.dag().heads_ancestors(self.to_id_set(&set)?)?;
let result = NameSet::from_spans_idmap(spans, self.clone_map()); let result = NameSet::from_spans_idmap(spans, self.clone_map());
#[cfg(test)] #[cfg(test)]
{ {
@ -492,46 +494,19 @@ impl<T: NameDagStorage> DagAlgorithm for T {
/// Calculates the "dag range" - vertexes reachable from both sides. /// Calculates the "dag range" - vertexes reachable from both sides.
fn range(&self, roots: NameSet, heads: NameSet) -> Result<NameSet> { fn range(&self, roots: NameSet, heads: NameSet) -> Result<NameSet> {
let roots = self.to_span_set(&roots)?; let roots = self.to_id_set(&roots)?;
let heads = self.to_span_set(&heads)?; let heads = self.to_id_set(&heads)?;
let spans = self.dag().range(roots, heads)?; let spans = self.dag().range(roots, heads)?;
Ok(NameSet::from_spans_idmap(spans, self.clone_map())) Ok(NameSet::from_spans_idmap(spans, self.clone_map()))
} }
/// Calculates the descendants of the given set. /// Calculates the descendants of the given set.
fn descendants(&self, set: NameSet) -> Result<NameSet> { fn descendants(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag().descendants(self.to_span_set(&set)?)?; let spans = self.dag().descendants(self.to_id_set(&set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.clone_map())) Ok(NameSet::from_spans_idmap(spans, self.clone_map()))
} }
} }
pub trait ToSpanSet {
/// Converts [`NameSet`] to [`SpanSet`].
fn to_span_set(&self, set: &NameSet) -> Result<SpanSet>;
}
impl<T: NameDagStorage> ToSpanSet for T {
/// Converts [`NameSet`] to [`SpanSet`].
fn to_span_set(&self, set: &NameSet) -> Result<SpanSet> {
// Fast path: extract SpanSet directly.
if let Some(set) = set.as_any().downcast_ref::<IdStaticSet>() {
if self.is_map_compatible(&set.map) {
return Ok(set.spans.clone());
}
}
// Slow path: iterate through the set and convert it to a non-lazy
// SpanSet.
let mut spans = SpanSet::empty();
for name in set.iter()? {
let name = name?;
let id = self.map().vertex_id(name)?;
spans.push(id);
}
Ok(spans)
}
}
impl<T> PrefixLookup for T impl<T> PrefixLookup for T
where where
T: NameDagStorage, T: NameDagStorage,
@ -693,7 +668,7 @@ fn is_ok_some<T>(value: Result<Option<T>>) -> bool {
} }
/// IdMap + IdDag backend for DagAlgorithm. /// IdMap + IdDag backend for DagAlgorithm.
pub trait NameDagStorage { pub trait NameDagStorage: IdMapEq {
type IdDagStore: IdDagStore; type IdDagStore: IdDagStore;
type IdMap: IdConvert; type IdMap: IdConvert;
@ -705,9 +680,6 @@ pub trait NameDagStorage {
/// (Cheaply) clone the map. /// (Cheaply) clone the map.
fn clone_map(&self) -> Arc<dyn IdConvert + Send + Sync>; fn clone_map(&self) -> Arc<dyn IdConvert + Send + Sync>;
/// (Cheaply) test if the map is compatible (same).
fn is_map_compatible(&self, other: &Arc<dyn IdConvert + Send + Sync>) -> bool;
} }
impl NameDagStorage for NameDag { impl NameDagStorage for NameDag {
@ -723,9 +695,6 @@ impl NameDagStorage for NameDag {
fn clone_map(&self) -> Arc<dyn IdConvert + Send + Sync> { fn clone_map(&self) -> Arc<dyn IdConvert + Send + Sync> {
self.snapshot_map.clone() self.snapshot_map.clone()
} }
fn is_map_compatible(&self, other: &Arc<dyn IdConvert + Send + Sync>) -> bool {
Arc::ptr_eq(other, &self.snapshot_map)
}
} }
impl NameDagStorage for MemNameDag { impl NameDagStorage for MemNameDag {
@ -741,6 +710,15 @@ impl NameDagStorage for MemNameDag {
fn clone_map(&self) -> Arc<dyn IdConvert + Send + Sync> { fn clone_map(&self) -> Arc<dyn IdConvert + Send + Sync> {
self.snapshot_map.clone() self.snapshot_map.clone()
} }
}
impl IdMapEq for NameDag {
fn is_map_compatible(&self, other: &Arc<dyn IdConvert + Send + Sync>) -> bool {
Arc::ptr_eq(other, &self.snapshot_map)
}
}
impl IdMapEq for MemNameDag {
fn is_map_compatible(&self, other: &Arc<dyn IdConvert + Send + Sync>) -> bool { fn is_map_compatible(&self, other: &Arc<dyn IdConvert + Send + Sync>) -> bool {
Arc::ptr_eq(other, &self.snapshot_map) Arc::ptr_eq(other, &self.snapshot_map)
} }

View File

@ -12,9 +12,13 @@ use crate::id::Group;
use crate::id::Id; use crate::id::Id;
use crate::id::VertexName; use crate::id::VertexName;
use crate::namedag::MemNameDag; use crate::namedag::MemNameDag;
use crate::nameset::id_lazy::IdLazySet;
use crate::nameset::id_static::IdStaticSet;
use crate::nameset::NameSet; use crate::nameset::NameSet;
use crate::IdSet;
use anyhow::Result; use anyhow::Result;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
/// DAG related read-only algorithms. /// DAG related read-only algorithms.
pub trait DagAlgorithm { pub trait DagAlgorithm {
@ -330,3 +334,43 @@ where
Ok(()) Ok(())
} }
} }
pub trait ToIdSet {
/// Converts [`NameSet`] to [`SpanSet`].
fn to_id_set(&self, set: &NameSet) -> Result<IdSet>;
}
pub trait IdMapEq {
/// (Cheaply) test if the map is compatible (same).
fn is_map_compatible(&self, other: &Arc<dyn IdConvert + Send + Sync>) -> bool;
}
impl<T: IdConvert + IdMapEq> ToIdSet for T {
/// Converts [`NameSet`] to [`IdSet`].
fn to_id_set(&self, set: &NameSet) -> Result<IdSet> {
// Fast path: extract IdSet from IdStaticSet.
if let Some(set) = set.as_any().downcast_ref::<IdStaticSet>() {
if self.is_map_compatible(&set.map) {
return Ok(set.spans.clone());
}
}
// Convert IdLazySet to IdStaticSet. Bypass hash lookups.
if let Some(set) = set.as_any().downcast_ref::<IdLazySet>() {
if self.is_map_compatible(&set.map) {
let set: IdStaticSet = set.to_static()?;
return Ok(set.spans);
}
}
// Slow path: iterate through the set and convert it to a non-lazy
// IdSet. Does not bypass hash lookups.
let mut spans = IdSet::empty();
for name in set.iter()? {
let name = name?;
let id = self.vertex_id(name)?;
spans.push(id);
}
Ok(spans)
}
}