#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct HlcTimestamp {
pub physical_ms: u64,
pub logical: u16,
}
impl HlcTimestamp {
pub const ZERO: HlcTimestamp = HlcTimestamp { physical_ms: 0, logical: 0 };
pub fn to_u64(self) -> u64 {
(self.physical_ms << 16) | self.logical as u64
}
pub fn from_u64(v: u64) -> Self {
Self {
physical_ms: v >> 16,
logical: (v & 0xFFFF) as u16,
}
}
pub fn to_coord(self) -> u32 {
(self.to_u64() >> 32) as u32
}
}
pub struct HlcClock {
last: HlcTimestamp,
}
impl HlcClock {
pub fn new() -> Self {
Self { last: HlcTimestamp::ZERO }
}
pub fn tick(&mut self, now_ms: u64) -> HlcTimestamp {
if now_ms > self.last.physical_ms {
self.last = HlcTimestamp { physical_ms: now_ms, logical: 0 };
} else {
self.last = HlcTimestamp {
physical_ms: self.last.physical_ms,
logical: self.last.logical.saturating_add(1),
};
}
self.last
}
pub fn receive(&mut self, remote: HlcTimestamp, now_ms: u64) -> HlcTimestamp {
let max_physical = self.last.physical_ms.max(remote.physical_ms).max(now_ms);
self.last = if max_physical == self.last.physical_ms
&& max_physical == remote.physical_ms
{
HlcTimestamp {
physical_ms: max_physical,
logical: self.last.logical.max(remote.logical).saturating_add(1),
}
} else if max_physical == self.last.physical_ms {
HlcTimestamp {
physical_ms: max_physical,
logical: self.last.logical.saturating_add(1),
}
} else if max_physical == remote.physical_ms {
HlcTimestamp {
physical_ms: max_physical,
logical: remote.logical.saturating_add(1),
}
} else {
HlcTimestamp { physical_ms: max_physical, logical: 0 }
};
self.last
}
}
impl Default for HlcClock {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tick_is_monotonic() {
let mut clk = HlcClock::new();
let t1 = clk.tick(1000);
let t2 = clk.tick(1000); let t3 = clk.tick(2000);
assert!(t1 < t2);
assert!(t2 < t3);
}
#[test]
fn roundtrip_u64() {
let ts = HlcTimestamp { physical_ms: 1_700_000_000_000, logical: 42 };
assert_eq!(HlcTimestamp::from_u64(ts.to_u64()), ts);
}
}