nautilus_network/ratelimiter/
nanos.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! A time-keeping abstraction (nanoseconds) that works for storing in an atomic integer.
17
18use std::{
19    fmt::Debug,
20    ops::{Add, Div, Mul},
21    prelude::v1::*,
22    time::Duration,
23};
24
25use super::clock;
26
27/// A number of nanoseconds from a reference point.
28///
29/// Nanos can not represent durations >584 years, but hopefully that
30/// should not be a problem in real-world applications.
31#[derive(PartialEq, Eq, Default, Clone, Copy, PartialOrd, Ord)]
32pub struct Nanos(u64);
33
34impl Nanos {
35    pub const fn as_u64(self) -> u64 {
36        self.0
37    }
38}
39
40/// Nanos as used by Jitter and other std-only features.
41#[cfg(feature = "std")]
42impl Nanos {
43    pub const fn new(u: u64) -> Self {
44        Self(u)
45    }
46}
47
48impl From<Duration> for Nanos {
49    fn from(d: Duration) -> Self {
50        // This will panic:
51        Self(
52            d.as_nanos()
53                .try_into()
54                .expect("Duration is longer than 584 years"),
55        )
56    }
57}
58
59impl Debug for Nanos {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
61        let d = Duration::from_nanos(self.0);
62        write!(f, "Nanos({d:?})")
63    }
64}
65
66impl Add<Self> for Nanos {
67    type Output = Self;
68
69    fn add(self, rhs: Self) -> Self::Output {
70        Self(self.0 + rhs.0)
71    }
72}
73
74impl Mul<u64> for Nanos {
75    type Output = Self;
76
77    fn mul(self, rhs: u64) -> Self::Output {
78        Self(self.0 * rhs)
79    }
80}
81
82impl Div<Self> for Nanos {
83    type Output = u64;
84
85    fn div(self, rhs: Self) -> Self::Output {
86        self.0 / rhs.0
87    }
88}
89
90impl From<u64> for Nanos {
91    fn from(u: u64) -> Self {
92        Self(u)
93    }
94}
95
96impl From<Nanos> for u64 {
97    fn from(n: Nanos) -> Self {
98        n.0
99    }
100}
101
102impl From<Nanos> for Duration {
103    fn from(n: Nanos) -> Self {
104        Self::from_nanos(n.0)
105    }
106}
107
108impl Nanos {
109    #[inline]
110    pub const fn saturating_sub(self, rhs: Self) -> Self {
111        Self(self.0.saturating_sub(rhs.0))
112    }
113}
114
115impl clock::Reference for Nanos {
116    #[inline]
117    fn duration_since(&self, earlier: Self) -> Nanos {
118        (*self as Self).saturating_sub(earlier)
119    }
120
121    #[inline]
122    fn saturating_sub(&self, duration: Nanos) -> Self {
123        (*self as Self).saturating_sub(duration)
124    }
125}
126
127impl Add<Duration> for Nanos {
128    type Output = Self;
129
130    fn add(self, other: Duration) -> Self {
131        let other: Self = other.into();
132        self + other
133    }
134}
135
136////////////////////////////////////////////////////////////////////////////////
137// Tests
138////////////////////////////////////////////////////////////////////////////////
139#[cfg(all(feature = "std", test))]
140mod test {
141    use std::time::Duration;
142
143    use super::*;
144
145    #[test]
146    fn nanos_impls() {
147        let n = Nanos::new(20);
148        assert_eq!("Nanos(20ns)", format!("{n:?}"));
149    }
150
151    #[test]
152    fn nanos_arith_coverage() {
153        let n = Nanos::new(20);
154        let n_half = Nanos::new(10);
155        assert_eq!(n / n_half, 2);
156        assert_eq!(30, (n + Duration::from_nanos(10)).as_u64());
157
158        assert_eq!(n_half.saturating_sub(n), Nanos::new(0));
159        assert_eq!(n.saturating_sub(n_half), n_half);
160        assert_eq!(clock::Reference::saturating_sub(&n_half, n), Nanos::new(0));
161    }
162}