use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(feature = "wasm")]
static MONOTONIC_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Copy)]
pub struct PortableInstant {
#[cfg(not(feature = "wasm"))]
inner: std::time::Instant,
#[cfg(feature = "wasm")]
counter: u64,
}
impl PortableInstant {
#[cfg(not(feature = "wasm"))]
pub fn now() -> Self {
Self {
inner: std::time::Instant::now(),
}
}
#[cfg(feature = "wasm")]
pub fn now() -> Self {
let counter = MONOTONIC_COUNTER.fetch_add(1, Ordering::SeqCst);
Self { counter }
}
#[cfg(not(feature = "wasm"))]
pub fn elapsed_micros(&self) -> u64 {
self.inner.elapsed().as_micros() as u64
}
#[cfg(feature = "wasm")]
pub fn elapsed_micros(&self) -> u64 {
let current = MONOTONIC_COUNTER.load(Ordering::SeqCst);
current.saturating_sub(self.counter)
}
#[cfg(not(feature = "wasm"))]
pub fn elapsed(&self) -> std::time::Duration {
self.inner.elapsed()
}
#[cfg(feature = "wasm")]
pub fn elapsed(&self) -> std::time::Duration {
std::time::Duration::from_micros(self.elapsed_micros())
}
#[cfg(not(feature = "wasm"))]
pub fn duration_since(&self, earlier: Self) -> std::time::Duration {
self.inner.duration_since(earlier.inner)
}
#[cfg(feature = "wasm")]
pub fn duration_since(&self, earlier: Self) -> std::time::Duration {
let diff = self.counter.saturating_sub(earlier.counter);
std::time::Duration::from_micros(diff)
}
}
impl Default for PortableInstant {
fn default() -> Self {
Self::now()
}
}
#[derive(Debug, Clone, Copy)]
pub struct PortableTimestamp {
pub secs: u64,
}
impl PortableTimestamp {
#[cfg(not(feature = "wasm"))]
pub fn now() -> Self {
let secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
Self { secs }
}
#[cfg(feature = "wasm")]
pub fn now() -> Self {
let secs = MONOTONIC_COUNTER.fetch_add(1, Ordering::SeqCst);
Self { secs }
}
pub fn as_secs(&self) -> u64 {
self.secs
}
}
impl Default for PortableTimestamp {
fn default() -> Self {
Self::now()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_portable_instant() {
let start = PortableInstant::now();
let _sum: u64 = (0..1000).sum();
let elapsed = start.elapsed_micros();
#[cfg(not(feature = "wasm"))]
assert!(elapsed >= 0);
}
#[test]
fn test_portable_timestamp() {
let ts1 = PortableTimestamp::now();
let ts2 = PortableTimestamp::now();
assert!(ts2.secs >= ts1.secs);
}
#[test]
fn test_instant_ordering() {
let t1 = PortableInstant::now();
let t2 = PortableInstant::now();
let d = t2.duration_since(t1);
assert!(d.as_micros() >= 0);
}
}