use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp(pub(crate) u64);
impl Timestamp {
pub const ZERO: Timestamp = Timestamp(0);
pub fn as_nanos(&self) -> u64 {
self.0
}
pub fn as_millis(&self) -> u64 {
self.0 / 1_000_000
}
pub fn as_secs_f64(&self) -> f64 {
self.0 as f64 / 1_000_000_000.0
}
pub fn duration_since(&self, other: Timestamp) -> u64 {
self.0.saturating_sub(other.0)
}
pub fn add_nanos(&self, nanos: u64) -> Timestamp {
Timestamp(self.0.saturating_add(nanos))
}
pub fn add_millis(&self, millis: u64) -> Timestamp {
Timestamp(self.0.saturating_add(millis * 1_000_000))
}
pub fn add_secs(&self, secs: u64) -> Timestamp {
Timestamp(self.0.saturating_add(secs * 1_000_000_000))
}
}
pub trait Clock: Send + Sync {
fn now(&self) -> Timestamp;
}
pub struct RealClock {
epoch: Instant,
}
impl RealClock {
pub fn new() -> Self {
Self {
epoch: Instant::now(),
}
}
}
impl Default for RealClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for RealClock {
fn now(&self) -> Timestamp {
let elapsed = self.epoch.elapsed();
Timestamp(elapsed.as_nanos() as u64)
}
}
pub struct MockClock {
nanos: AtomicU64,
}
impl MockClock {
pub fn new() -> Self {
Self {
nanos: AtomicU64::new(0),
}
}
pub fn at(timestamp: Timestamp) -> Self {
Self {
nanos: AtomicU64::new(timestamp.0),
}
}
pub fn advance_nanos(&self, nanos: u64) {
self.nanos.fetch_add(nanos, Ordering::Release);
}
pub fn advance_ms(&self, ms: u64) {
self.advance_nanos(ms * 1_000_000);
}
pub fn advance_secs(&self, secs: u64) {
self.advance_nanos(secs * 1_000_000_000);
}
pub fn set(&self, timestamp: Timestamp) {
self.nanos.store(timestamp.0, Ordering::Release);
}
}
impl Default for MockClock {
fn default() -> Self {
Self::new()
}
}
impl Clock for MockClock {
fn now(&self) -> Timestamp {
Timestamp(self.nanos.load(Ordering::Acquire))
}
}
pub(crate) fn default_clock() -> Arc<dyn Clock> {
Arc::new(RealClock::new())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timestamp_arithmetic() {
let t = Timestamp::ZERO;
assert_eq!(t.as_nanos(), 0);
assert_eq!(t.as_millis(), 0);
let t2 = t.add_millis(1500);
assert_eq!(t2.as_millis(), 1500);
assert_eq!(t2.as_nanos(), 1_500_000_000);
let t3 = t2.add_secs(2);
assert_eq!(t3.as_millis(), 3500);
assert_eq!(t3.duration_since(t2), 2_000_000_000);
assert_eq!(t2.duration_since(t3), 0); }
#[test]
fn mock_clock_advances() {
let clock = MockClock::new();
assert_eq!(clock.now(), Timestamp::ZERO);
clock.advance_ms(100);
assert_eq!(clock.now().as_millis(), 100);
clock.advance_secs(1);
assert_eq!(clock.now().as_millis(), 1100);
}
#[test]
fn mock_clock_set() {
let clock = MockClock::new();
clock.set(Timestamp(5_000_000_000));
assert_eq!(clock.now().as_secs_f64(), 5.0);
}
#[test]
fn real_clock_monotonic() {
let clock = RealClock::new();
let t1 = clock.now();
let t2 = clock.now();
assert!(t2 >= t1);
}
#[test]
fn timestamp_ordering() {
let a = Timestamp(100);
let b = Timestamp(200);
assert!(a < b);
assert!(b > a);
assert_eq!(a, Timestamp(100));
}
}