qubit_clock/clock/nano_monotonic_clock.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! High-precision monotonic clock implementation.
11//!
12//! This module provides [`NanoMonotonicClock`], a clock implementation that
13//! provides nanosecond-precision monotonic time measurements.
14//!
15
16use crate::{
17 Clock,
18 NanoClock,
19};
20use chrono::Utc;
21use std::time::{
22 Duration,
23 Instant,
24};
25
26/// A clock implementation that provides nanosecond-precision monotonic time.
27///
28/// This clock combines the monotonic guarantees of
29/// [`MonotonicClock`](crate::MonotonicClock) with the nanosecond precision
30/// of [`NanoClock`]. It uses `std::time::Instant` as its time source and
31/// stores the base time with nanosecond precision.
32///
33/// # Use Cases
34///
35/// - High-precision performance testing
36/// - Microbenchmarking
37/// - Scenarios requiring nanosecond-level time measurements
38///
39/// # Thread Safety
40///
41/// This type is completely thread-safe as all fields are immutable after
42/// creation.
43///
44/// # Examples
45///
46/// ```
47/// use qubit_clock::{NanoClock, NanoMonotonicClock};
48///
49/// let clock = NanoMonotonicClock::new();
50/// let start = clock.nanos();
51///
52/// // Perform some operation
53/// for _ in 0..1000 {
54/// // Some work
55/// }
56///
57/// let elapsed = clock.nanos() - start;
58/// println!("Elapsed: {} ns", elapsed);
59/// ```
60///
61#[derive(Debug, Clone)]
62pub struct NanoMonotonicClock {
63 /// The base instant when this clock was created.
64 instant_base: Instant,
65 /// The system time (seconds part) when this clock was created.
66 system_time_base_seconds: i64,
67 /// The system time (nanoseconds part) when this clock was created.
68 system_time_base_nanos: u32,
69}
70
71impl NanoMonotonicClock {
72 /// Creates a new `NanoMonotonicClock`.
73 ///
74 /// The clock records the current instant and system time (with nanosecond
75 /// precision) as its base point. All subsequent time queries will be
76 /// calculated relative to this base point.
77 ///
78 /// # Returns
79 ///
80 /// A new `NanoMonotonicClock` instance.
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use qubit_clock::NanoMonotonicClock;
86 ///
87 /// let clock = NanoMonotonicClock::new();
88 /// ```
89 ///
90 #[inline]
91 pub fn new() -> Self {
92 let now = Utc::now();
93 NanoMonotonicClock {
94 instant_base: Instant::now(),
95 system_time_base_seconds: now.timestamp(),
96 system_time_base_nanos: now.timestamp_subsec_nanos(),
97 }
98 }
99
100 /// Returns the elapsed monotonic duration since this clock was created.
101 ///
102 /// This value is based purely on `Instant` and is not affected by system
103 /// time adjustments.
104 ///
105 /// # Returns
106 ///
107 /// The elapsed monotonic duration.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use qubit_clock::NanoMonotonicClock;
113 /// use std::thread;
114 /// use std::time::Duration;
115 ///
116 /// let clock = NanoMonotonicClock::new();
117 /// thread::sleep(Duration::from_millis(10));
118 /// assert!(clock.elapsed() >= Duration::from_millis(10));
119 /// ```
120 #[inline]
121 pub fn elapsed(&self) -> Duration {
122 self.instant_base.elapsed()
123 }
124
125 /// Returns the elapsed monotonic time in nanoseconds since creation.
126 ///
127 /// Unlike [`NanoClock::nanos`](crate::NanoClock::nanos), this value does
128 /// not include a wall-clock epoch anchor and is intended for interval
129 /// measurement.
130 ///
131 /// # Returns
132 ///
133 /// The elapsed monotonic nanoseconds, saturated at `i128::MAX`.
134 ///
135 /// # Examples
136 ///
137 /// ```
138 /// use qubit_clock::NanoMonotonicClock;
139 /// use std::thread;
140 /// use std::time::Duration;
141 ///
142 /// let clock = NanoMonotonicClock::new();
143 /// thread::sleep(Duration::from_millis(10));
144 /// assert!(clock.monotonic_nanos() >= 10_000_000);
145 /// ```
146 #[inline]
147 pub fn monotonic_nanos(&self) -> i128 {
148 let elapsed_nanos = self.elapsed().as_nanos();
149 i128::try_from(elapsed_nanos).unwrap_or(i128::MAX)
150 }
151}
152
153impl Default for NanoMonotonicClock {
154 #[inline]
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl Clock for NanoMonotonicClock {
161 #[inline]
162 fn millis(&self) -> i64 {
163 let millis = self.nanos().div_euclid(1_000_000);
164 i64::try_from(millis).unwrap_or(i64::MAX)
165 }
166}
167
168impl NanoClock for NanoMonotonicClock {
169 #[inline]
170 fn nanos(&self) -> i128 {
171 let elapsed_nanos = self.monotonic_nanos();
172 let base_nanos =
173 (self.system_time_base_seconds as i128) * 1_000_000_000 + (self.system_time_base_nanos as i128);
174 base_nanos.saturating_add(elapsed_nanos)
175 }
176}