diff --git a/crates/loro-core/src/dag.rs b/crates/loro-core/src/dag.rs index af096b6d..614a88d6 100644 --- a/crates/loro-core/src/dag.rs +++ b/crates/loro-core/src/dag.rs @@ -11,6 +11,7 @@ use crate::{ change::Lamport, id::{ClientID, Counter, ID}, span::{CounterSpan, IdSpan}, + version::VersionVector, }; pub trait DagNode { @@ -92,11 +93,16 @@ pub(crate) trait Dag { ) } + /// TODO: we probably need cache to speedup this + #[inline] + fn get_vv(&self, id: ID) -> VersionVector { + get_version_vector(&|id| self.get(id).map(|x| x as &dyn DagNode), id) + } + #[inline] fn find_path(&self, from: ID, to: ID) -> Option { let mut ans: Option = None; - #[inline] fn get_rev_path(target: ID, from: ID, to_from_map: &FxHashMap) -> Vec { let mut last_visited: Option = None; let mut a_rev_path = vec![]; @@ -158,6 +164,43 @@ pub(crate) trait Dag { } } +fn get_version_vector<'a, Get>(get: &'a Get, id: ID) -> VersionVector +where + Get: Fn(ID) -> Option<&'a dyn DagNode>, +{ + let mut vv = VersionVector::new(); + let mut visited: FxHashSet = FxHashSet::default(); + vv.insert(id.client_id, id.counter + 1); + let node = get(id).unwrap(); + + if node.deps().is_empty() { + return vv; + } + + let mut stack = Vec::new(); + for dep in node.deps() { + stack.push(dep); + } + + while !stack.is_empty() { + let node_id = *stack.pop().unwrap(); + let node = get(node_id).unwrap(); + let node_id_start = node.dag_id_start(); + if !visited.contains(&node_id_start) { + vv.try_update_end(node_id); + for dep in node.deps() { + if !visited.contains(dep) { + stack.push(dep); + } + } + + visited.insert(node_id_start); + } + } + + vv +} + fn find_common_ancestor<'a, F, G>(get: &'a F, a_id: ID, b_id: ID, mut on_found: G) -> Option where F: Fn(ID) -> Option<&'a dyn DagNode>, diff --git a/crates/loro-core/src/dag/test.rs b/crates/loro-core/src/dag/test.rs index 1bf5d44b..e6cc9892 100644 --- a/crates/loro-core/src/dag/test.rs +++ b/crates/loro-core/src/dag/test.rs @@ -216,6 +216,35 @@ struct Interaction { len: usize, } +mod get_version_vector { + use super::*; + + #[test] + fn vv() { + let mut a = TestDag::new(0); + let mut b = TestDag::new(1); + a.push(1); + b.push(1); + a.merge(&b); + a.push(1); + let actual = a.get_vv(ID::new(0, 0)); + assert_eq!(actual, vec![ID::new(0, 0)].into()); + let actual = a.get_vv(ID::new(0, 1)); + assert_eq!(actual, vec![ID::new(0, 1), ID::new(1, 0)].into()); + + let mut c = TestDag::new(2); + c.merge(&a); + b.push(1); + c.merge(&b); + c.push(1); + let actual = c.get_vv(ID::new(2, 0)); + assert_eq!( + actual, + vec![ID::new(0, 1), ID::new(1, 1), ID::new(2, 0)].into() + ); + } +} + mod find_path { use super::*; diff --git a/crates/loro-core/src/version.rs b/crates/loro-core/src/version.rs index 153ac9e8..c3a4184e 100644 --- a/crates/loro-core/src/version.rs +++ b/crates/loro-core/src/version.rs @@ -1,8 +1,85 @@ +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; + use fxhash::FxHashMap; -use crate::{change::Lamport, ClientID}; +use crate::{ + change::Lamport, + id::{Counter, ID}, + ClientID, +}; -pub type VersionVector = FxHashMap; +#[repr(transparent)] +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct VersionVector(FxHashMap); + +impl Deref for VersionVector { + type Target = FxHashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for VersionVector { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl VersionVector { + #[inline] + pub fn new() -> Self { + Self(FxHashMap::default()) + } + + #[inline] + pub fn set_end(&mut self, id: ID) { + self.0.insert(id.client_id, id.counter + 1); + } + + /// update the end counter of the given client, if the end is greater + /// return whether updated + #[inline] + pub fn try_update_end(&mut self, id: ID) -> bool { + if let Some(end) = self.0.get_mut(&id.client_id) { + if *end < id.counter { + *end = id.counter + 1; + true + } else { + false + } + } else { + self.0.insert(id.client_id, id.counter + 1); + true + } + } +} + +impl Default for VersionVector { + fn default() -> Self { + Self::new() + } +} + +impl From> for VersionVector { + fn from(map: FxHashMap) -> Self { + Self(map) + } +} + +impl From> for VersionVector { + fn from(vec: Vec) -> Self { + let mut vv = VersionVector::new(); + for id in vec { + vv.set_end(id); + } + + vv + } +} #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] pub(crate) struct TotalOrderStamp {