lcpfs 2026.1.102

LCP File System - A ZFS-inspired copy-on-write filesystem for Rust
// Copyright 2025 LunaOS Contributors
// SPDX-License-Identifier: Apache-2.0

//! # Unified Time Provider
//!
//! Provides consistent timestamp APIs across LCPFS modules.
//!
//! ## Overview
//!
//! This module centralizes time operations to avoid duplicate implementations
//! scattered across the codebase. It provides:
//!
//! - Unix timestamps for metadata (created, modified times)
//! - Monotonic counters for ordering and performance measurement
//! - Hardware timestamp access via architecture-specific code
//!
//! ## Usage
//!
//! ```rust,ignore
//! use lcpfs::time;
//!
//! // Get current Unix timestamp (seconds since epoch)
//! let now = time::now();
//!
//! // Get monotonic counter (for ordering, not wall-clock time)
//! let tick = time::monotonic();
//!
//! // Get high-resolution timestamp (CPU cycles or similar)
//! let tsc = time::high_resolution();
//! ```
//!
//! ## Kernel vs Userspace
//!
//! When running in kernel mode (LunaOS), timestamps come from the kernel's
//! time subsystem. In userspace testing, we use placeholder implementations
//! or system calls.

use core::sync::atomic::{AtomicU64, Ordering};

// ═══════════════════════════════════════════════════════════════════════════════
// GLOBAL STATE
// ═══════════════════════════════════════════════════════════════════════════════

/// Epoch offset for converting monotonic to Unix time.
///
/// Set once at initialization from kernel or system time.
static EPOCH_OFFSET: AtomicU64 = AtomicU64::new(0);

/// Monotonic counter for unique ordering.
///
/// Guarantees strictly increasing values even if called in tight loops.
static MONOTONIC_COUNTER: AtomicU64 = AtomicU64::new(0);

/// Last returned timestamp for deduplication.
static LAST_TIMESTAMP: AtomicU64 = AtomicU64::new(0);

// ═══════════════════════════════════════════════════════════════════════════════
// PUBLIC API
// ═══════════════════════════════════════════════════════════════════════════════

/// Get current Unix timestamp in seconds since epoch (1970-01-01 00:00:00 UTC).
///
/// This is the primary timestamp for file metadata (created, modified times).
///
/// # Returns
///
/// Seconds since Unix epoch. In kernel mode, this is accurate wall-clock time.
/// In testing/userspace without a clock, returns monotonic counter.
///
/// # Example
///
/// ```rust,ignore
/// let created_at = time::now();
/// file.set_created(created_at);
/// ```
#[inline]
pub fn now() -> u64 {
    let epoch = EPOCH_OFFSET.load(Ordering::Relaxed);
    if epoch > 0 {
        // We have a calibrated epoch, use monotonic + offset
        let mono = monotonic_raw();
        epoch + mono
    } else {
        // No calibration yet, use monotonic counter as fallback
        // This ensures timestamps are at least unique and ordered
        monotonic()
    }
}

/// Get current Unix timestamp in nanoseconds since epoch.
///
/// Higher precision version of [`now()`] for sub-second accuracy.
///
/// # Returns
///
/// Nanoseconds since Unix epoch.
#[inline]
pub fn now_ns() -> u64 {
    // If we have high-resolution time, use it
    // Otherwise, convert seconds to nanoseconds
    now() * 1_000_000_000
}

/// Get monotonic counter value.
///
/// Returns a strictly increasing value suitable for:
/// - Transaction ordering
/// - Event sequencing
/// - Performance measurement
///
/// **Note**: This is NOT wall-clock time. Values may not correlate with
/// real time and should only be used for ordering/comparison.
///
/// # Guarantees
///
/// - Strictly increasing (never returns same value twice)
/// - Thread-safe
/// - Survives system time changes
///
/// # Example
///
/// ```rust,ignore
/// let start = time::monotonic();
/// // ... do work ...
/// let end = time::monotonic();
/// assert!(end > start);
/// ```
#[inline]
pub fn monotonic() -> u64 {
    MONOTONIC_COUNTER.fetch_add(1, Ordering::SeqCst)
}

/// Get raw monotonic counter without incrementing.
///
/// Useful when you need the current position without consuming a value.
#[inline]
pub fn monotonic_raw() -> u64 {
    MONOTONIC_COUNTER.load(Ordering::SeqCst)
}

/// Get high-resolution timestamp from hardware.
///
/// Returns CPU timestamp counter (TSC on x86_64) or similar hardware counter.
/// This is the highest resolution timing available, typically sub-nanosecond.
///
/// **Warning**: This value is:
/// - Not synchronized across CPUs (may vary per-core)
/// - Not calibrated to wall-clock time
/// - Platform-dependent resolution
///
/// Use only for fine-grained timing and entropy mixing.
///
/// # Example
///
/// ```rust,ignore
/// let t0 = time::high_resolution();
/// // ... fast operation ...
/// let t1 = time::high_resolution();
/// let cycles = t1 - t0;
/// ```
#[inline]
pub fn high_resolution() -> u64 {
    crate::arch::get_timestamp()
}

/// Initialize time subsystem with calibration.
///
/// Should be called once at filesystem mount with the current Unix time.
/// This calibrates the monotonic counter to wall-clock time.
///
/// # Arguments
///
/// * `unix_time` - Current Unix timestamp in seconds
///
/// # Example
///
/// ```rust,ignore
/// // At mount time, get time from kernel
/// let kernel_time = kernel::get_time_seconds();
/// time::init(kernel_time);
/// ```
pub fn init(unix_time: u64) {
    let current_mono = MONOTONIC_COUNTER.load(Ordering::SeqCst);
    // Epoch offset = unix_time - current_monotonic
    // So: now() = epoch_offset + monotonic = unix_time
    let offset = unix_time.saturating_sub(current_mono);
    EPOCH_OFFSET.store(offset, Ordering::SeqCst);
}

/// Check if time subsystem is initialized.
///
/// Returns true if [`init()`] has been called with a valid timestamp.
#[inline]
pub fn is_initialized() -> bool {
    EPOCH_OFFSET.load(Ordering::Relaxed) > 0
}

/// Get unique timestamp (never duplicates).
///
/// Similar to [`now()`] but guarantees uniqueness even when called
/// multiple times per second. Uses the monotonic counter as tiebreaker.
///
/// Useful for generating unique IDs with embedded timestamps.
///
/// # Returns
///
/// Tuple of (seconds, counter) that together form a unique value.
#[inline]
pub fn unique() -> (u64, u64) {
    let secs = now();
    let counter = monotonic();
    (secs, counter)
}

/// Ensure timestamps are monotonically increasing.
///
/// Returns the given timestamp if it's greater than the last returned value,
/// otherwise returns last + 1 to maintain ordering.
///
/// Useful for imported data that may have out-of-order timestamps.
#[inline]
pub fn ensure_monotonic(ts: u64) -> u64 {
    loop {
        let last = LAST_TIMESTAMP.load(Ordering::SeqCst);
        let new_ts = ts.max(last + 1);
        if LAST_TIMESTAMP
            .compare_exchange_weak(last, new_ts, Ordering::SeqCst, Ordering::Relaxed)
            .is_ok()
        {
            return new_ts;
        }
    }
}

// ═══════════════════════════════════════════════════════════════════════════════
// DURATION HELPERS
// ═══════════════════════════════════════════════════════════════════════════════

/// Seconds per minute.
pub const SECS_PER_MIN: u64 = 60;

/// Seconds per hour.
pub const SECS_PER_HOUR: u64 = 60 * SECS_PER_MIN;

/// Seconds per day.
pub const SECS_PER_DAY: u64 = 24 * SECS_PER_HOUR;

/// Seconds per week.
pub const SECS_PER_WEEK: u64 = 7 * SECS_PER_DAY;

/// Convert days to seconds.
#[inline]
pub const fn days(n: u64) -> u64 {
    n * SECS_PER_DAY
}

/// Convert hours to seconds.
#[inline]
pub const fn hours(n: u64) -> u64 {
    n * SECS_PER_HOUR
}

/// Convert minutes to seconds.
#[inline]
pub const fn minutes(n: u64) -> u64 {
    n * SECS_PER_MIN
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_monotonic_increasing() {
        let a = monotonic();
        let b = monotonic();
        let c = monotonic();
        assert!(b > a);
        assert!(c > b);
    }

    #[test]
    fn test_now_returns_something() {
        let ts = now();
        // Without init, now() falls back to monotonic
        // Just check it returns a value
        assert!(ts < u64::MAX);
    }

    #[test]
    fn test_init_calibration() {
        // Reset for test
        EPOCH_OFFSET.store(0, Ordering::SeqCst);

        // Initialize with a known time
        let known_time = 1704067200; // 2024-01-01 00:00:00 UTC
        init(known_time);

        assert!(is_initialized());

        // now() should return approximately the init time
        let ts = now();
        // Allow some slack for the monotonic counter advancement
        assert!(ts >= known_time);
        assert!(ts < known_time + 1000); // Within 1000 ticks
    }

    #[test]
    fn test_ensure_monotonic() {
        LAST_TIMESTAMP.store(0, Ordering::SeqCst);

        // First call should return the timestamp
        let a = ensure_monotonic(100);
        assert!(a >= 100);

        // Backward timestamp should be corrected
        let b = ensure_monotonic(50);
        assert!(b > a);

        // Forward timestamp should work
        let c = ensure_monotonic(200);
        assert!(c >= 200);
        assert!(c > b);
    }

    #[test]
    fn test_duration_constants() {
        assert_eq!(SECS_PER_MIN, 60);
        assert_eq!(SECS_PER_HOUR, 3600);
        assert_eq!(SECS_PER_DAY, 86400);
        assert_eq!(days(1), 86400);
        assert_eq!(hours(1), 3600);
        assert_eq!(minutes(1), 60);
    }
}