1
//! Internal parts used for sealing.
2
//!
3
//! This module primarily consists of the internal [`SaturatingTime`] trait, an
4
//! unstable abstraction used internally to implement the main logic behind
5
//! this.
6
//!
7
//! Normal users should not be using this.
8

            
9
use std::{
10
    cmp,
11
    sync::LazyLock,
12
    time::{Duration, Instant, SystemTime},
13
};
14

            
15
/// The maximum value of [`SystemTime`] for this platform.
16
static MAX_SYSTEM_TIME: LazyLock<SystemTime> = LazyLock::new(find_max);
17

            
18
/// The minimum value of [`SystemTime`] for this platform.
19
static MIN_SYSTEM_TIME: LazyLock<SystemTime> = LazyLock::new(find_min);
20

            
21
/// The maximum value of [`Instant`] for this platform.
22
static MAX_INSTANT: LazyLock<Instant> = LazyLock::new(find_max);
23

            
24
/// The minimum value of [`Instant`] for this platform.
25
static MIN_INSTANT: LazyLock<Instant> = LazyLock::new(find_min);
26

            
27
/// An internal trait implementing the actual magic behind this.
28
pub trait SaturatingTime: Sized + Copy {
29
    /// Anchor method to obtain an instance of this type.
30
    fn anchor() -> Self;
31

            
32
    /// Returns the maximum value of this type.
33
    fn max_value() -> Self;
34

            
35
    /// Returns the minimum value of this type.
36
    fn min_value() -> Self;
37

            
38
    /// Performs a checked addition on this type.
39
    fn checked_add(&self, duration: Duration) -> Option<Self>;
40

            
41
    /// Performs a checked subtraction on this type.
42
    fn checked_sub(&self, duration: Duration) -> Option<Self>;
43

            
44
    /// Performs a checked time delta on this type.
45
    fn checked_duration_since(&self, earlier: Self) -> Option<Duration>;
46
}
47

            
48
impl SaturatingTime for SystemTime {
49
3
    fn anchor() -> Self {
50
3
        Self::UNIX_EPOCH
51
3
    }
52

            
53
13
    fn max_value() -> Self {
54
13
        *MAX_SYSTEM_TIME
55
13
    }
56

            
57
13
    fn min_value() -> Self {
58
13
        *MIN_SYSTEM_TIME
59
13
    }
60

            
61
549391
    fn checked_add(&self, duration: Duration) -> Option<Self> {
62
549391
        Self::checked_add(self, duration)
63
549391
    }
64

            
65
3613
    fn checked_sub(&self, duration: Duration) -> Option<Self> {
66
3613
        Self::checked_sub(self, duration)
67
3613
    }
68

            
69
548483
    fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
70
548483
        Self::duration_since(self, earlier).ok()
71
548483
    }
72
}
73

            
74
impl SaturatingTime for Instant {
75
3
    fn anchor() -> Self {
76
        use web_time_compat::InstantExt;
77
3
        Self::get()
78
3
    }
79

            
80
13
    fn max_value() -> Self {
81
13
        *MAX_INSTANT
82
13
    }
83

            
84
13
    fn min_value() -> Self {
85
13
        *MIN_INSTANT
86
13
    }
87

            
88
155
    fn checked_add(&self, duration: Duration) -> Option<Self> {
89
155
        Self::checked_add(self, duration)
90
155
    }
91

            
92
144
    fn checked_sub(&self, duration: Duration) -> Option<Self> {
93
144
        Self::checked_sub(self, duration)
94
144
    }
95

            
96
    /// DO NOT USE!
97
    ///
98
    /// Instead, override the top-level provided implementation with the already
99
    /// existing [`Instant::saturating_duration_since()`].
100
    fn checked_duration_since(&self, _earlier: Self) -> Option<Duration> {
101
        unreachable!()
102
    }
103
}
104

            
105
/// Finds the value for [`SaturatingTime::max_value()`].
106
2
fn find_max<T: SaturatingTime>() -> T {
107
2
    find_limit(T::checked_add)
108
2
}
109

            
110
/// Finds the value for [`SaturatingTime::min_value()`].
111
2
fn find_min<T: SaturatingTime>() -> T {
112
2
    find_limit(T::checked_sub)
113
2
}
114

            
115
/// Internal algorithm of [`find_max()`] and [`find_min()`].
116
///
117
/// It works by performing `f` with a very large [`Duration`] onto
118
/// [`SaturatingTime::anchor()`] until this call returns [`None`], in which case
119
/// this [`Duration`] gets halved.  This process is repeated until `f` returns
120
/// [`None`] and the [`Duration`] has reached 1ns.
121
///
122
/// # Algorithm
123
///
124
/// 1. Set `step` to `INITIAL_STEP` and `res` to [`SaturatingTime::anchor()`].
125
/// 2. Call `f(&res, step)`.
126
///     1. If [`Some`], set `res` to the returned value and continue.
127
///     2. If [`None`] and `step == 1ns`, return `res`.
128
///     3. Else, set `step` to `MAX{1ns, step / 2}` and continue.
129
4
fn find_limit<T, F>(f: F) -> T
130
4
where
131
4
    T: SaturatingTime,
132
4
    F: Fn(&T, Duration) -> Option<T>,
133
{
134
    const INITIAL_STEP: Duration = Duration::new(1_000_000_000_000_000_000, 0);
135
    const ONE_NS: Duration = Duration::new(0, 1);
136

            
137
    // (1) Set step to INITIAL_STEP and res to T::anchor().
138
4
    let mut step = INITIAL_STEP;
139
4
    let mut res = T::anchor();
140

            
141
    loop {
142
        // (2) Call f().
143
579
        let next = f(&res, step);
144
579
        match next {
145
219
            Some(st) => {
146
219
                // (2.1) If Some, set res to the returned value and continue.
147
219
                res = st;
148
219
            }
149
            None => {
150
360
                if step == ONE_NS {
151
                    // (2.2) If None and step == 1ns, return res.
152
4
                    return res;
153
356
                } else {
154
356
                    // (2.3) Else, set step to MAX{1ns, step / 2}.
155
356
                    step = cmp::max(ONE_NS, step / 2);
156
356
                }
157
            }
158
        }
159
    }
160
4
}
161

            
162
#[cfg(test)]
163
mod tests {
164
    // @@ begin test lint list maintained by maint/add_warning @@
165
    #![allow(clippy::bool_assert_comparison)]
166
    #![allow(clippy::clone_on_copy)]
167
    #![allow(clippy::dbg_macro)]
168
    #![allow(clippy::mixed_attributes_style)]
169
    #![allow(clippy::print_stderr)]
170
    #![allow(clippy::print_stdout)]
171
    #![allow(clippy::single_char_pattern)]
172
    #![allow(clippy::unwrap_used)]
173
    #![allow(clippy::unchecked_time_subtraction)]
174
    #![allow(clippy::useless_vec)]
175
    #![allow(clippy::needless_pass_by_value)]
176
    #![allow(clippy::string_slice)] // See arti#2571
177
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
178

            
179
    use std::{
180
        fmt::Debug,
181
        ops::{Add, Sub},
182
    };
183

            
184
    use super::*;
185

            
186
    /// Checks whether the minimum and maximum values are correct.
187
    fn min_max<T>()
188
    where
189
        T: SaturatingTime
190
            + PartialEq
191
            + Debug
192
            + Add<Duration, Output = T>
193
            + Sub<Duration, Output = T>,
194
    {
195
        assert_eq!(
196
            T::max_value().checked_add(Duration::ZERO),
197
            Some(T::max_value())
198
        );
199
        assert_eq!(T::max_value().checked_add(Duration::new(0, 1)), None);
200
        assert_eq!(
201
            T::max_value().checked_sub(Duration::ZERO),
202
            Some(T::max_value())
203
        );
204
        assert_eq!(
205
            T::max_value().checked_sub(Duration::new(0, 1)),
206
            Some(T::max_value() - Duration::new(0, 1))
207
        );
208

            
209
        assert_eq!(
210
            T::min_value().checked_sub(Duration::ZERO),
211
            Some(T::min_value())
212
        );
213
        assert_eq!(T::min_value().checked_sub(Duration::new(0, 1)), None);
214
        assert_eq!(
215
            T::min_value().checked_add(Duration::ZERO),
216
            Some(T::min_value())
217
        );
218
        assert_eq!(
219
            T::min_value().checked_add(Duration::new(0, 1)),
220
            Some(T::min_value() + Duration::new(0, 1))
221
        );
222
    }
223

            
224
    /// Verifies [`SystemTime::min_value()`] and [`SystemTime::max_value()`] are
225
    /// correct.
226
    #[test]
227
    fn system_time_min_max() {
228
        min_max::<SystemTime>();
229
    }
230

            
231
    /// Verifies [`Instant::min_value()`] and [`Instant::max_value()`] are
232
    /// correct.
233
    #[test]
234
    fn instant_min_max() {
235
        min_max::<Instant>();
236
    }
237

            
238
    /// Verifies [`SystemTime::min_value()`] and [`SystemTime::max_value()`] are
239
    /// correct on Unix systems.
240
    #[cfg(target_family = "unix")]
241
    #[test]
242
    fn system_time_min_max_unix() {
243
        assert_eq!(
244
            SystemTime::max_value(),
245
            SystemTime::UNIX_EPOCH + Duration::new(i64::MAX as u64, 999_999_999)
246
        );
247
        assert_eq!(
248
            SystemTime::min_value(),
249
            SystemTime::UNIX_EPOCH - Duration::new(i64::MAX as u64 + 1, 0)
250
        );
251
    }
252

            
253
    /// Verifies that [`Instant::min_value()`] and [`Instant::max_value()`] are
254
    /// correct on Unix systems.
255
    #[test]
256
    #[cfg(target_family = "unix")]
257
    fn instant_min_max_unix() {
258
        // Using format is not nice but I cannot see a better way for now.
259
        assert_eq!(
260
            format!("{:?}", Instant::max_value()),
261
            format!(
262
                "Instant {{ tv_sec: {}, tv_nsec: {} }}",
263
                i64::MAX,
264
                999_999_999
265
            )
266
        );
267

            
268
        assert_eq!(
269
            format!("{:?}", Instant::min_value()),
270
            format!("Instant {{ tv_sec: {}, tv_nsec: {} }}", i64::MIN, 0)
271
        );
272
    }
273
}