use std::ops::{Add, Sub};
use malloc_size_of_derive::MallocSizeOf;
use serde::{Deserialize, Serialize};
use time::Duration;
#[derive(
Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize,
)]
pub struct CrossProcessInstant {
value: u64,
}
impl CrossProcessInstant {
pub fn now() -> Self {
Self {
value: platform::now(),
}
}
pub fn epoch() -> Self {
Self { value: 0 }
}
}
impl Sub for CrossProcessInstant {
type Output = Duration;
fn sub(self, rhs: Self) -> Self::Output {
Duration::nanoseconds(self.value as i64 - rhs.value as i64)
}
}
impl Add<Duration> for CrossProcessInstant {
type Output = Self;
fn add(self, rhs: Duration) -> Self::Output {
Self {
value: self.value + rhs.whole_nanoseconds() as u64,
}
}
}
impl Sub<Duration> for CrossProcessInstant {
type Output = Self;
fn sub(self, rhs: Duration) -> Self::Output {
Self {
value: self.value - rhs.whole_nanoseconds() as u64,
}
}
}
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
mod platform {
use libc::timespec;
#[expect(unsafe_code)]
pub(super) fn now() -> u64 {
let time = unsafe {
let mut time: timespec = std::mem::zeroed();
libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time);
time
};
(time.tv_sec as u64) * 1000000000 + (time.tv_nsec as u64)
}
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
mod platform {
use std::sync::LazyLock;
use mach2::mach_time::{mach_absolute_time, mach_timebase_info};
#[expect(unsafe_code)]
fn timebase_info() -> &'static mach_timebase_info {
static TIMEBASE_INFO: LazyLock<mach_timebase_info> = LazyLock::new(|| {
let mut timebase_info = mach_timebase_info { numer: 0, denom: 0 };
unsafe { mach_timebase_info(&mut timebase_info) };
timebase_info
});
&TIMEBASE_INFO
}
#[expect(unsafe_code)]
pub(super) fn now() -> u64 {
let timebase_info = timebase_info();
let absolute_time = unsafe { mach_absolute_time() };
absolute_time * timebase_info.numer as u64 / timebase_info.denom as u64
}
}
#[cfg(target_os = "windows")]
mod platform {
use std::sync::atomic::{AtomicU64, Ordering};
use windows_sys::Win32::System::Performance::{
QueryPerformanceCounter, QueryPerformanceFrequency,
};
#[expect(unsafe_code)]
fn frequency() -> i64 {
static FREQUENCY: AtomicU64 = AtomicU64::new(0);
let cached = FREQUENCY.load(Ordering::Relaxed);
if cached != 0 {
return cached as i64;
}
let mut frequency = 0;
let result = unsafe { QueryPerformanceFrequency(&mut frequency) };
if result == 0 {
return 0;
}
FREQUENCY.store(frequency as u64, Ordering::Relaxed);
frequency
}
#[expect(unsafe_code)]
pub(super) fn now() -> u64 {
let mut counter_value = 0;
unsafe { QueryPerformanceCounter(&mut counter_value) };
fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 {
let q = value / denom;
let r = value % denom;
q * numer + r * numer / denom
}
static NANOSECONDS_PER_SECOND: u64 = 1_000_000_000;
mul_div_u64(
counter_value as u64,
NANOSECONDS_PER_SECOND,
frequency() as u64,
)
}
}