use crate::CrdtMerge;
use fxhash::FxHashMap;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct VectorClock {
pub clocks: FxHashMap<String, u64>,
}
impl VectorClock {
pub fn new() -> Self {
Self::default()
}
pub fn increment(&mut self, actor: &str) {
*self.clocks.entry(actor.to_string()).or_insert(0) += 1;
}
pub fn get(&self, actor: &str) -> u64 {
*self.clocks.get(actor).unwrap_or(&0)
}
pub fn merge_clock(&mut self, other: &VectorClock) {
for (actor, &count) in &other.clocks {
let entry = self.clocks.entry(actor.clone()).or_insert(0);
*entry = std::cmp::max(*entry, count);
}
}
pub fn happened_before(&self, other: &VectorClock) -> bool {
let mut strictly_less = false;
for (actor, &count) in &self.clocks {
let other_count = other.get(actor);
if count > other_count {
return false;
}
if count < other_count {
strictly_less = true;
}
}
if !strictly_less {
for (actor, &count) in &other.clocks {
if !self.clocks.contains_key(actor) && count > 0 {
strictly_less = true;
break;
}
}
}
strictly_less
}
pub fn is_concurrent(&self, other: &VectorClock) -> bool {
!self.happened_before(other) && !other.happened_before(self) && self != other
}
pub fn causal_cmp(&self, other: &VectorClock) -> Option<Ordering> {
if self == other {
Some(Ordering::Equal)
} else if self.happened_before(other) {
Some(Ordering::Less)
} else if other.happened_before(self) {
Some(Ordering::Greater)
} else {
None }
}
}
impl CrdtMerge for VectorClock {
fn merge(&mut self, other: &Self) {
self.merge_clock(other);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vector_clock_ordering() {
let mut a = VectorClock::new();
a.increment("node1");
let mut b = a.clone();
b.increment("node1");
assert!(a.happened_before(&b));
assert!(!b.happened_before(&a));
assert_eq!(a.causal_cmp(&b), Some(Ordering::Less));
let mut c = a.clone();
c.increment("node2");
assert!(!b.happened_before(&c));
assert!(!c.happened_before(&b));
assert!(b.is_concurrent(&c));
assert_eq!(b.causal_cmp(&c), None);
}
#[test]
fn test_merge() {
let mut a = VectorClock::new();
a.increment("node1");
let mut b = VectorClock::new();
b.increment("node2");
a.merge(&b); assert_eq!(a.get("node1"), 1);
assert_eq!(a.get("node2"), 1);
}
}