use crate::{
id::SecretId,
peer_list::{PeerIndex, PeerIndexMap, PeerList},
};
use fnv::FnvHashSet;
use itertools::{EitherOrBoth, Itertools};
use std::{
cmp,
collections::{BTreeMap, BTreeSet},
fmt::{self, Debug, Formatter},
iter,
};
pub(super) type ForkMap = BTreeMap<usize, IndexSet>;
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct IndexSet(FnvHashSet<usize>);
impl IndexSet {
pub fn new(index: usize) -> Self {
IndexSet(iter::once(index).collect())
}
pub fn union(&self, other: &Self) -> Self {
IndexSet(self.0.union(&other.0).cloned().collect())
}
pub fn insert(&self, index: usize) -> Self {
let mut set = self.0.clone();
let _ = set.insert(index);
IndexSet(set)
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn contains(&self, index: usize) -> bool {
self.0.contains(&index)
}
pub fn is_disjoint(&self, other: &Self) -> bool {
self.0.is_disjoint(&other.0)
}
}
impl Debug for IndexSet {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
let values = self.0.iter().collect::<BTreeSet<_>>();
write!(formatter, "{}", values.iter().format(", "))
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct AncestorInfo {
pub last: usize,
pub forks: ForkMap,
}
impl AncestorInfo {
pub fn new() -> Self {
Self {
last: 0,
forks: ForkMap::new(),
}
}
}
pub(super) fn compute_ancestor_info<S: SecretId>(
creator: PeerIndex,
index_by_creator: usize,
self_parent_info: Option<&PeerIndexMap<AncestorInfo>>,
other_parent_info: Option<&PeerIndexMap<AncestorInfo>>,
peer_list: &PeerList<S>,
) -> PeerIndexMap<AncestorInfo> {
let mut result = match (self_parent_info, other_parent_info) {
(Some(self_parent_info), Some(other_parent_info)) => {
merge_ancestor_info_maps(self_parent_info, other_parent_info)
}
(Some(self_parent_info), None) => self_parent_info.clone(),
(None, Some(other_parent_info)) => other_parent_info.clone(),
(None, None) => PeerIndexMap::new(),
};
let info = result.entry(creator).or_insert_with(AncestorInfo::new);
info.last = index_by_creator;
let fork_index = peer_list.events_by_index(creator, index_by_creator).count();
if fork_index > 0 {
let _ = info
.forks
.insert(index_by_creator, IndexSet::new(fork_index));
}
result
}
fn merge_ancestor_info_maps(
map0: &PeerIndexMap<AncestorInfo>,
map1: &PeerIndexMap<AncestorInfo>,
) -> PeerIndexMap<AncestorInfo> {
map0.iter()
.merge_join_by(map1.iter(), |(peer_index0, _), (peer_index1, _)| {
peer_index0.cmp(peer_index1)
})
.map(|either| match either {
EitherOrBoth::Left((peer_index, info)) | EitherOrBoth::Right((peer_index, info)) => {
(peer_index, info.clone())
}
EitherOrBoth::Both((peer_index, info0), (_, info1)) => {
(peer_index, merge_ancestor_infos(info0, info1))
}
})
.collect()
}
fn merge_ancestor_infos(lhs_info: &AncestorInfo, rhs_info: &AncestorInfo) -> AncestorInfo {
AncestorInfo {
last: cmp::max(lhs_info.last, rhs_info.last),
forks: merge_fork_maps(lhs_info, rhs_info),
}
}
fn merge_fork_maps(lhs_info: &AncestorInfo, rhs_info: &AncestorInfo) -> ForkMap {
lhs_info
.forks
.iter()
.merge_join_by(rhs_info.forks.iter(), |(key0, _), (key1, _)| key0.cmp(key1))
.map(|either| match either {
EitherOrBoth::Left((&index_by_creator, lhs_fork_set)) => (
index_by_creator,
merge_with_implicit_fork_set(lhs_fork_set, index_by_creator, rhs_info.last),
),
EitherOrBoth::Right((&index_by_creator, rhs_fork_set)) => (
index_by_creator,
merge_with_implicit_fork_set(rhs_fork_set, index_by_creator, lhs_info.last),
),
EitherOrBoth::Both((&index_by_creator, lhs_fork_set), (_, rhs_fork_set)) => {
(index_by_creator, lhs_fork_set.union(rhs_fork_set))
}
})
.collect()
}
fn merge_with_implicit_fork_set(
forks: &IndexSet,
index_by_creator: usize,
last_ancestor: usize,
) -> IndexSet {
if last_ancestor >= index_by_creator {
forks.insert(0)
} else {
forks.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn merge_fork_maps_of_events_that_are_not_descendants_of_any_fork() {
let a_info = AncestorInfo {
last: 0,
forks: btree_map![],
};
let b_info = AncestorInfo {
last: 0,
forks: btree_map![],
};
assert_eq!(merge_fork_maps(&a_info, &b_info), btree_map![])
}
#[test]
fn merge_fork_maps_of_events_that_are_descendants_of_different_fork_sides() {
let a_info = AncestorInfo {
last: 1,
forks: btree_map![],
};
let b_info = AncestorInfo {
last: 1,
forks: btree_map![1 => IndexSet::new(1)],
};
assert_eq!(
merge_fork_maps(&a_info, &b_info),
btree_map![1 => IndexSet::new(0).insert(1)]
)
}
#[test]
fn merge_fork_maps_of_events_that_are_descendants_of_same_fork_side() {
let a_info = AncestorInfo {
last: 1,
forks: btree_map![1 => IndexSet::new(1)],
};
let b_info = AncestorInfo {
last: 1,
forks: btree_map![1 => IndexSet::new(1)],
};
assert_eq!(
merge_fork_maps(&a_info, &b_info),
btree_map![1 => IndexSet::new(1)]
)
}
#[test]
fn merge_fork_maps_of_one_event_that_is_descendant_of_fork_and_another_that_isnt() {
let a_info = AncestorInfo {
last: 1,
forks: btree_map![],
};
let b_info = AncestorInfo {
last: 2,
forks: btree_map![2 => IndexSet::new(1)],
};
assert_eq!(
merge_fork_maps(&a_info, &b_info),
btree_map![2 => IndexSet::new(1)]
)
}
}