use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Hlc {
pub wall_ms: u64,
pub logical: u32,
}
impl Hlc {
pub const ZERO: Hlc = Hlc {
wall_ms: 0,
logical: 0,
};
pub fn tick(self, now_ms: u64) -> Hlc {
let wall = now_ms.max(self.wall_ms);
let logical = if wall == self.wall_ms {
self.logical + 1
} else {
0
};
Hlc {
wall_ms: wall,
logical,
}
}
pub fn merge(self, remote: Hlc, now_ms: u64) -> Hlc {
let wall = now_ms.max(self.wall_ms).max(remote.wall_ms);
let logical = match (wall == self.wall_ms, wall == remote.wall_ms) {
(true, true) => self.logical.max(remote.logical) + 1,
(true, false) => self.logical + 1,
(false, true) => remote.logical + 1,
(false, false) => 0,
};
Hlc {
wall_ms: wall,
logical,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tick_monotonic_when_clock_stalls() {
let a = Hlc::ZERO.tick(1000);
let b = a.tick(1000);
assert!(b > a);
assert_eq!(b.wall_ms, 1000);
assert_eq!(b.logical, 1);
}
#[test]
fn merge_dominates_both_inputs() {
let local = Hlc {
wall_ms: 100,
logical: 2,
};
let remote = Hlc {
wall_ms: 200,
logical: 5,
};
let merged = local.merge(remote, 150);
assert!(merged > local);
assert!(merged > remote);
}
}