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
40impl Nanos {
41    pub const fn new(u: u64) -> Self {
42        Self(u)
43    }
44}
45
46impl From<Duration> for Nanos {
47    fn from(d: Duration) -> Self {
48        // This will panic:
49        Self(
50            d.as_nanos()
51                .try_into()
52                .expect("Duration is longer than 584 years"),
53        )
54    }
55}
56
57impl Debug for Nanos {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
59        let d = Duration::from_nanos(self.0);
60        write!(f, "Nanos({d:?})")
61    }
62}
63
64impl Add<Self> for Nanos {
65    type Output = Self;
66
67    fn add(self, rhs: Self) -> Self::Output {
68        Self(self.0 + rhs.0)
69    }
70}
71
72impl Mul<u64> for Nanos {
73    type Output = Self;
74
75    fn mul(self, rhs: u64) -> Self::Output {
76        Self(self.0 * rhs)
77    }
78}
79
80impl Div<Self> for Nanos {
81    type Output = u64;
82
83    fn div(self, rhs: Self) -> Self::Output {
84        self.0 / rhs.0
85    }
86}
87
88impl From<u64> for Nanos {
89    fn from(u: u64) -> Self {
90        Self(u)
91    }
92}
93
94impl From<Nanos> for u64 {
95    fn from(n: Nanos) -> Self {
96        n.0
97    }
98}
99
100impl From<Nanos> for Duration {
101    fn from(n: Nanos) -> Self {
102        Self::from_nanos(n.0)
103    }
104}
105
106impl Nanos {
107    #[inline]
108    pub const fn saturating_sub(self, rhs: Self) -> Self {
109        Self(self.0.saturating_sub(rhs.0))
110    }
111}
112
113impl clock::Reference for Nanos {
114    #[inline]
115    fn duration_since(&self, earlier: Self) -> Nanos {
116        (*self as Self).saturating_sub(earlier)
117    }
118
119    #[inline]
120    fn saturating_sub(&self, duration: Nanos) -> Self {
121        (*self as Self).saturating_sub(duration)
122    }
123}
124
125impl Add<Duration> for Nanos {
126    type Output = Self;
127
128    fn add(self, other: Duration) -> Self {
129        let other: Self = other.into();
130        self + other
131    }
132}
133
134////////////////////////////////////////////////////////////////////////////////
135// Tests
136////////////////////////////////////////////////////////////////////////////////
137#[cfg(test)]
138mod test {
139    use std::time::Duration;
140
141    use rstest::rstest;
142
143    use super::*;
144
145    #[rstest]
146    fn nanos_impls() {
147        let n = Nanos::new(20);
148        assert_eq!("Nanos(20ns)", format!("{n:?}"));
149    }
150
151    #[rstest]
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}