emberkv-core 0.4.8

Core engine for ember: keyspace, data types, sharding
Documentation
//! Compact monotonic time utilities.
//!
//! Uses a process-local monotonic clock for timestamps that are smaller
//! than std::time::Instant (8 bytes vs 16 bytes for Option<Instant>).

use std::sync::OnceLock;
use std::time::Instant;

/// Returns current monotonic time in milliseconds since process start.
#[inline]
pub fn now_ms() -> u64 {
    static START: OnceLock<Instant> = OnceLock::new();
    let start = START.get_or_init(Instant::now);
    start.elapsed().as_millis() as u64
}

/// Returns current monotonic time in seconds since process start, as u32.
///
/// Used for LRU last-access tracking where millisecond precision isn't
/// needed and the smaller type improves cache-line packing. Wraps after
/// ~136 years — well beyond any realistic process lifetime.
#[inline]
pub fn now_secs() -> u32 {
    static START: OnceLock<Instant> = OnceLock::new();
    let start = START.get_or_init(Instant::now);
    start.elapsed().as_secs() as u32
}

/// Sentinel value meaning "no expiry".
pub const NO_EXPIRY: u64 = 0;

/// Returns true if the given expiry timestamp has passed.
#[inline]
pub fn is_expired(expires_at_ms: u64) -> bool {
    expires_at_ms != NO_EXPIRY && now_ms() >= expires_at_ms
}

/// Converts a Duration to an absolute expiry timestamp.
#[inline]
pub fn expiry_from_duration(ttl: Option<std::time::Duration>) -> u64 {
    ttl.map(|d| {
        let ms = d.as_millis().min(u64::MAX as u128) as u64;
        now_ms().saturating_add(ms)
    })
    .unwrap_or(NO_EXPIRY)
}

/// Returns remaining TTL in seconds, or None if no expiry.
#[inline]
pub fn remaining_secs(expires_at_ms: u64) -> Option<u64> {
    remaining(expires_at_ms, 1000)
}

/// Returns remaining TTL in milliseconds, or None if no expiry.
#[inline]
pub fn remaining_ms(expires_at_ms: u64) -> Option<u64> {
    remaining(expires_at_ms, 1)
}

/// Shared implementation for remaining TTL with a unit divisor.
#[inline]
fn remaining(expires_at_ms: u64, divisor: u64) -> Option<u64> {
    if expires_at_ms == NO_EXPIRY {
        None
    } else {
        let now = now_ms();
        Some(expires_at_ms.saturating_sub(now) / divisor)
    }
}