use serde::{Deserialize, Serialize};
use std::cell::Cell;
use std::ops::{Add, Rem, Sub};
#[cfg(all(not(unix), not(windows)))]
use std::sync::OnceLock;
#[cfg(all(not(unix), not(windows)))]
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
pub struct Seconds(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
pub struct Microseconds(pub u64);
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[cfg_attr(feature = "allocative", derive(allocative::Allocative))]
pub struct EntryTimestamps {
pub source_realtime_usec: Option<u64>,
pub entry_realtime_usec: Option<u64>,
pub entry_monotonic_usec: Option<u64>,
}
impl EntryTimestamps {
pub fn with_source_realtime_usec(mut self, ts: u64) -> Self {
self.source_realtime_usec = Some(ts);
self
}
pub fn with_entry_realtime_usec(mut self, ts: u64) -> Self {
self.entry_realtime_usec = Some(ts);
self
}
pub fn with_entry_monotonic_usec(mut self, ts: u64) -> Self {
self.entry_monotonic_usec = Some(ts);
self
}
}
impl Seconds {
pub fn new(seconds: u32) -> Self {
Self(seconds)
}
pub fn now() -> Self {
Self(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time must be after UNIX_EPOCH")
.as_secs() as u32,
)
}
pub fn get(self) -> u32 {
self.0
}
pub fn to_microseconds(self) -> Microseconds {
Microseconds(self.0 as u64 * 1_000_000)
}
pub fn saturating_add(self, other: Self) -> Self {
Seconds(self.0.saturating_add(other.0))
}
pub fn saturating_sub(self, other: Self) -> Self {
Seconds(self.0.saturating_sub(other.0))
}
pub fn checked_add(self, other: Self) -> Option<Self> {
self.0.checked_add(other.0).map(Seconds)
}
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.0.checked_sub(other.0).map(Seconds)
}
pub fn is_multiple_of(self, other: Self) -> bool {
other.0 != 0 && self.0 % other.0 == 0
}
}
impl Microseconds {
pub fn new(microseconds: u64) -> Self {
Self(microseconds)
}
pub fn now() -> Self {
Self(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system time must be after UNIX_EPOCH")
.as_micros() as u64,
)
}
pub fn get(self) -> u64 {
self.0
}
pub fn to_seconds(self) -> Seconds {
Seconds((self.0 / 1_000_000) as u32)
}
pub fn saturating_add(self, other: Self) -> Self {
Microseconds(self.0.saturating_add(other.0))
}
pub fn saturating_sub(self, other: Self) -> Self {
Microseconds(self.0.saturating_sub(other.0))
}
pub fn checked_add(self, other: Self) -> Option<Self> {
self.0.checked_add(other.0).map(Microseconds)
}
pub fn checked_sub(self, other: Self) -> Option<Self> {
self.0.checked_sub(other.0).map(Microseconds)
}
pub fn is_multiple_of(self, other: Self) -> bool {
other.0 != 0 && self.0 % other.0 == 0
}
}
impl From<Seconds> for Microseconds {
fn from(s: Seconds) -> Self {
s.to_microseconds()
}
}
impl From<u32> for Seconds {
fn from(s: u32) -> Self {
Seconds(s)
}
}
impl From<u64> for Microseconds {
fn from(us: u64) -> Self {
Microseconds(us)
}
}
impl Add for Seconds {
type Output = Self;
fn add(self, other: Self) -> Self {
Seconds(self.0 + other.0)
}
}
impl Sub for Seconds {
type Output = Self;
fn sub(self, other: Self) -> Self {
Seconds(self.0 - other.0)
}
}
impl Rem for Seconds {
type Output = Self;
fn rem(self, other: Self) -> Self {
Seconds(self.0 % other.0)
}
}
impl Add for Microseconds {
type Output = Self;
fn add(self, other: Self) -> Self {
Microseconds(self.0 + other.0)
}
}
impl Sub for Microseconds {
type Output = Self;
fn sub(self, other: Self) -> Self {
Microseconds(self.0 - other.0)
}
}
impl Rem for Microseconds {
type Output = Self;
fn rem(self, other: Self) -> Self {
Microseconds(self.0 % other.0)
}
}
impl std::fmt::Display for Seconds {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}s", self.0)
}
}
impl std::fmt::Display for Microseconds {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}µs", self.0)
}
}
#[derive(Debug)]
pub struct RealtimeClock {
max_seen: Cell<u64>,
}
impl RealtimeClock {
pub fn new() -> Self {
Self::with_initial(Microseconds::now())
}
pub fn with_initial(initial: Microseconds) -> Self {
Self {
max_seen: Cell::new(initial.get()),
}
}
pub fn now(&self) -> Microseconds {
let current = Microseconds::now();
let max = self.max_seen.get();
let next = if current.get() > max {
current.get()
} else {
max.saturating_add(1)
};
self.max_seen.set(next);
Microseconds::new(next)
}
pub fn observe(&self, candidate: Microseconds) -> Microseconds {
let max = self.max_seen.get();
let next = if candidate.get() > max {
candidate.get()
} else {
max.saturating_add(1)
};
self.max_seen.set(next);
Microseconds::new(next)
}
pub fn last_seen(&self) -> Microseconds {
Microseconds::new(self.max_seen.get())
}
}
impl Default for RealtimeClock {
fn default() -> Self {
Self::new()
}
}
pub fn monotonic_now() -> std::io::Result<Microseconds> {
#[cfg(unix)]
{
let mut ts = std::mem::MaybeUninit::<libc::timespec>::uninit();
let rc = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, ts.as_mut_ptr()) };
if rc != 0 {
return Err(std::io::Error::last_os_error());
}
let ts = unsafe { ts.assume_init() };
let seconds = u64::try_from(ts.tv_sec).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"CLOCK_MONOTONIC returned a negative seconds value",
)
})?;
let nanos = u64::try_from(ts.tv_nsec).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"CLOCK_MONOTONIC returned a negative nanoseconds value",
)
})?;
let micros = seconds
.checked_mul(1_000_000)
.and_then(|value| value.checked_add(nanos / 1_000))
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"CLOCK_MONOTONIC microseconds overflowed u64",
)
})?;
Ok(Microseconds::new(micros))
}
#[cfg(windows)]
{
use windows_sys::Win32::System::WindowsProgramming::QueryUnbiasedInterruptTime;
let mut ticks_100ns = 0u64;
let ok = unsafe { QueryUnbiasedInterruptTime(&mut ticks_100ns) };
if ok == 0 {
return Err(std::io::Error::last_os_error());
}
Ok(Microseconds::new(ticks_100ns / 10))
}
#[cfg(all(not(unix), not(windows)))]
{
static START: OnceLock<Instant> = OnceLock::new();
let elapsed = START.get_or_init(Instant::now).elapsed();
let micros = u64::try_from(elapsed.as_micros()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
"monotonic elapsed microseconds overflowed u64",
)
})?;
Ok(Microseconds::new(micros))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_seconds_to_microseconds() {
let seconds = Seconds::new(42);
let micros = seconds.to_microseconds();
assert_eq!(micros.get(), 42_000_000);
}
#[test]
fn test_microseconds_to_seconds() {
let micros = Microseconds::new(42_500_000);
let seconds = micros.to_seconds();
assert_eq!(seconds.get(), 42);
}
#[test]
fn test_conversion_roundtrip() {
let original = Seconds::new(100);
let roundtrip = original.to_microseconds().to_seconds();
assert_eq!(original, roundtrip);
}
#[test]
fn test_from_conversions() {
let s: Seconds = 42u32.into();
assert_eq!(s.get(), 42);
let us: Microseconds = 42000u64.into();
assert_eq!(us.get(), 42000);
}
#[test]
fn test_seconds_add() {
let a = Seconds::new(10);
let b = Seconds::new(20);
assert_eq!(a + b, Seconds::new(30));
}
#[test]
fn test_seconds_sub() {
let a = Seconds::new(30);
let b = Seconds::new(10);
assert_eq!(a - b, Seconds::new(20));
}
#[test]
#[should_panic]
fn test_seconds_sub_underflow() {
let a = Seconds::new(10);
let b = Seconds::new(20);
let _ = a - b; }
#[test]
fn test_seconds_rem() {
let a = Seconds::new(10);
let b = Seconds::new(3);
assert_eq!(a % b, Seconds::new(1));
}
#[test]
fn test_seconds_saturating_add() {
let a = Seconds::new(u32::MAX - 5);
let b = Seconds::new(10);
assert_eq!(a.saturating_add(b), Seconds::new(u32::MAX));
}
#[test]
fn test_seconds_saturating_sub() {
let a = Seconds::new(10);
let b = Seconds::new(20);
assert_eq!(a.saturating_sub(b), Seconds::new(0));
}
#[test]
fn test_seconds_checked_add() {
let a = Seconds::new(10);
let b = Seconds::new(20);
assert_eq!(a.checked_add(b), Some(Seconds::new(30)));
let c = Seconds::new(u32::MAX);
let d = Seconds::new(1);
assert_eq!(c.checked_add(d), None);
}
#[test]
fn test_seconds_checked_sub() {
let a = Seconds::new(30);
let b = Seconds::new(10);
assert_eq!(a.checked_sub(b), Some(Seconds::new(20)));
let c = Seconds::new(10);
let d = Seconds::new(20);
assert_eq!(c.checked_sub(d), None);
}
#[test]
fn test_seconds_is_multiple_of() {
let a = Seconds::new(60);
let b = Seconds::new(15);
assert!(a.is_multiple_of(b));
let c = Seconds::new(60);
let d = Seconds::new(17);
assert!(!c.is_multiple_of(d));
let e = Seconds::new(0);
let f = Seconds::new(10);
assert!(e.is_multiple_of(f));
let g = Seconds::new(10);
let h = Seconds::new(0);
assert!(!g.is_multiple_of(h)); }
#[test]
fn test_microseconds_add() {
let a = Microseconds::new(1000);
let b = Microseconds::new(2000);
assert_eq!(a + b, Microseconds::new(3000));
}
#[test]
fn test_microseconds_sub() {
let a = Microseconds::new(3000);
let b = Microseconds::new(1000);
assert_eq!(a - b, Microseconds::new(2000));
}
#[test]
#[should_panic]
fn test_microseconds_sub_underflow() {
let a = Microseconds::new(1000);
let b = Microseconds::new(2000);
let _ = a - b; }
#[test]
fn test_microseconds_rem() {
let a = Microseconds::new(1000);
let b = Microseconds::new(300);
assert_eq!(a % b, Microseconds::new(100));
}
#[test]
fn test_microseconds_saturating_add() {
let a = Microseconds::new(u64::MAX - 5);
let b = Microseconds::new(10);
assert_eq!(a.saturating_add(b), Microseconds::new(u64::MAX));
}
#[test]
fn test_microseconds_saturating_sub() {
let a = Microseconds::new(1000);
let b = Microseconds::new(2000);
assert_eq!(a.saturating_sub(b), Microseconds::new(0));
}
#[test]
fn test_microseconds_checked_add() {
let a = Microseconds::new(1000);
let b = Microseconds::new(2000);
assert_eq!(a.checked_add(b), Some(Microseconds::new(3000)));
let c = Microseconds::new(u64::MAX);
let d = Microseconds::new(1);
assert_eq!(c.checked_add(d), None);
}
#[test]
fn test_microseconds_checked_sub() {
let a = Microseconds::new(3000);
let b = Microseconds::new(1000);
assert_eq!(a.checked_sub(b), Some(Microseconds::new(2000)));
let c = Microseconds::new(1000);
let d = Microseconds::new(2000);
assert_eq!(c.checked_sub(d), None);
}
#[test]
fn test_microseconds_is_multiple_of() {
let a = Microseconds::new(60000);
let b = Microseconds::new(15000);
assert!(a.is_multiple_of(b));
let c = Microseconds::new(60000);
let d = Microseconds::new(17000);
assert!(!c.is_multiple_of(d));
let e = Microseconds::new(0);
let f = Microseconds::new(10000);
assert!(e.is_multiple_of(f));
let g = Microseconds::new(10000);
let h = Microseconds::new(0);
assert!(!g.is_multiple_of(h)); }
#[test]
fn test_realtime_clock_monotonic() {
let clock = RealtimeClock::new();
let t1 = clock.now();
let t2 = clock.now();
let t3 = clock.now();
assert!(t2 > t1);
assert!(t3 > t2);
}
#[test]
fn test_realtime_clock_with_initial() {
let initial = Microseconds::new(1000000);
let clock = RealtimeClock::with_initial(initial);
assert_eq!(clock.last_seen(), initial);
let t1 = clock.now();
assert!(t1 >= initial);
}
#[test]
fn test_realtime_clock_handles_same_time() {
let initial = Microseconds::new(1000000);
let clock = RealtimeClock::with_initial(initial);
let t1 = clock.observe(initial);
let t2 = clock.observe(initial);
assert!(t2 > t1);
assert_eq!(t2.get() - t1.get(), 1); }
#[test]
fn test_realtime_clock_last_seen() {
let clock = RealtimeClock::new();
let t1 = clock.now();
assert_eq!(clock.last_seen(), t1);
let t2 = clock.now();
assert_eq!(clock.last_seen(), t2);
}
#[test]
fn test_realtime_clock_forward_jump() {
let past = Microseconds::new(1000000);
let clock = RealtimeClock::with_initial(past);
let t1 = clock.now();
assert!(t1.get() > past.get());
}
#[test]
fn test_realtime_clock_observe_preserves_monotonicity() {
let clock = RealtimeClock::with_initial(Microseconds::new(1_000_000));
let t1 = clock.observe(Microseconds::new(900_000));
assert_eq!(t1.get(), 1_000_001);
let t2 = clock.observe(Microseconds::new(1_000_001));
assert_eq!(t2.get(), 1_000_002);
let t3 = clock.observe(Microseconds::new(1_500_000));
assert_eq!(t3.get(), 1_500_000);
}
#[test]
fn test_monotonic_now_is_ordered() {
let first = monotonic_now().expect("monotonic clock");
let second = monotonic_now().expect("monotonic clock");
assert!(second >= first);
}
}