use std::collections::HashMap;
#[derive(Debug, Clone, Default)]
pub struct VectorClock {
entries: HashMap<u64, u64>,
}
impl VectorClock {
pub fn new() -> Self {
Self::default()
}
pub fn set(&mut self, peer_id: u64, counter: u64) {
self.entries.insert(peer_id, counter);
}
pub fn get(&self, peer_id: u64) -> u64 {
self.entries.get(&peer_id).copied().unwrap_or(0)
}
pub fn advance(&mut self, peer_id: u64, counter: u64) {
let entry = self.entries.entry(peer_id).or_insert(0);
if counter > *entry {
*entry = counter;
}
}
pub fn merge(&mut self, other: &VectorClock) {
for (&peer, &counter) in &other.entries {
self.advance(peer, counter);
}
}
pub fn dominates(&self, other: &VectorClock) -> bool {
other
.entries
.iter()
.all(|(&peer, &counter)| self.get(peer) >= counter)
}
pub fn diff(&self, remote: &VectorClock) -> Vec<(u64, u64, u64)> {
let mut diffs = Vec::new();
for (&peer, &remote_counter) in &remote.entries {
let local_counter = self.get(peer);
if remote_counter > local_counter {
diffs.push((peer, remote_counter, local_counter));
}
}
diffs
}
pub fn to_wire(&self) -> HashMap<String, u64> {
self.entries
.iter()
.map(|(&peer, &counter)| (format!("{peer:016x}"), counter))
.collect()
}
pub fn from_wire(wire: &HashMap<String, u64>) -> Self {
let entries = wire
.iter()
.filter_map(|(hex, &counter)| {
u64::from_str_radix(hex, 16)
.ok()
.map(|peer| (peer, counter))
})
.collect();
Self { entries }
}
pub fn peer_count(&self) -> usize {
self.entries.len()
}
pub fn peers(&self) -> Vec<u64> {
self.entries.keys().copied().collect()
}
pub fn as_map(&self) -> &HashMap<u64, u64> {
&self.entries
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_clock_is_empty() {
let c = VectorClock::new();
assert_eq!(c.peer_count(), 0);
assert_eq!(c.get(1), 0);
}
#[test]
fn set_and_get() {
let mut c = VectorClock::new();
c.set(1, 42);
assert_eq!(c.get(1), 42);
assert_eq!(c.get(2), 0);
}
#[test]
fn advance_only_increases() {
let mut c = VectorClock::new();
c.set(1, 10);
c.advance(1, 5); assert_eq!(c.get(1), 10);
c.advance(1, 15); assert_eq!(c.get(1), 15);
}
#[test]
fn merge_takes_max() {
let mut a = VectorClock::new();
a.set(1, 10);
a.set(2, 20);
let mut b = VectorClock::new();
b.set(1, 15);
b.set(3, 30);
a.merge(&b);
assert_eq!(a.get(1), 15); assert_eq!(a.get(2), 20); assert_eq!(a.get(3), 30); }
#[test]
fn dominates() {
let mut a = VectorClock::new();
a.set(1, 10);
a.set(2, 20);
let mut b = VectorClock::new();
b.set(1, 5);
b.set(2, 20);
assert!(a.dominates(&b));
assert!(!b.dominates(&a)); }
#[test]
fn diff_finds_ahead_peers() {
let mut local = VectorClock::new();
local.set(1, 10);
local.set(2, 20);
let mut remote = VectorClock::new();
remote.set(1, 15); remote.set(2, 20); remote.set(3, 5);
let diffs = local.diff(&remote);
assert_eq!(diffs.len(), 2);
assert!(diffs.iter().any(|&(p, r, l)| p == 1 && r == 15 && l == 10));
assert!(diffs.iter().any(|&(p, r, l)| p == 3 && r == 5 && l == 0));
}
#[test]
fn wire_roundtrip() {
let mut c = VectorClock::new();
c.set(1, 42);
c.set(255, 100);
let wire = c.to_wire();
let restored = VectorClock::from_wire(&wire);
assert_eq!(restored.get(1), 42);
assert_eq!(restored.get(255), 100);
}
}