devela 0.27.0

A development layer of coherence.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
// devela::phys::time::delta::basic
//
// TOC
// - basic methods
// - additional methods
// - impl_system_instant

use super::*;

/// # Basic methods
impl TimeDelta {
    /// A duration of zero time.
    pub const ZERO: TimeDelta = TimeDelta { secs: 0, nanos: 0 };

    /// The minimum possible duration. Or the "most negative" duration.
    pub const MIN: TimeDelta = TimeDelta { secs: i64::MIN, nanos: -(NANOS_PER_SEC - 1) };

    /// The maximum possible duration.
    pub const MAX: TimeDelta = TimeDelta { secs: i64::MAX, nanos: NANOS_PER_SEC - 1 };

    /// Creates a new `TimeDelta` from the given number of whole seconds and additional nanoseconds.
    ///
    /// If the absolute value of the nanoseconds is greater than or equal to
    /// 1 second, then the excess balances into the number of whole seconds.
    ///
    /// # Panics
    /// When the absolute value of the nanoseconds is greater than or equal
    /// to 1 second and the excess that carries over to the number of whole
    /// seconds overflows `i64`.
    ///
    /// This never panics when `nanos` is less than `1_000_000_000`.
    pub const fn new(mut secs: i64, mut nanos: i32) -> TimeDelta {
        // When |nanos| exceeds 1 second, we balance the excess up to seconds.
        if !(-NANOS_PER_SEC < nanos && nanos < NANOS_PER_SEC) {
            // Never wraps or panics because NANOS_PER_SEC!={0,-1}.
            let addsecs = nanos / NANOS_PER_SEC;
            secs = match secs.checked_add(addsecs as i64) {
                Some(secs) => secs,
                None => panic!("nanoseconds overflowed seconds in TimeDelta::new"),
            };
            // Never wraps or panics because NANOS_PER_SEC!={0,-1}.
            nanos %= NANOS_PER_SEC;
        }
        // At this point, we're done if either unit is zero or if they have the same sign.
        if nanos == 0 || secs == 0 || secs.signum() == (nanos.signum() as i64) {
            return TimeDelta::new_unchecked(secs, nanos);
        }
        // Otherwise, the only work we have to do is to balance negative nanos
        // into positive seconds, or positive nanos into negative seconds.
        if secs < 0 {
            debug_assert!(nanos > 0);
            // Never wraps because adding +1 to a negative i64 never overflows.
            //
            // MSRV(1.79): Consider using `unchecked_add` here.
            secs += 1;
            // Never wraps because subtracting +1_000_000_000 from a positive i32 never overflows.
            //
            // MSRV(1.79): Consider using `unchecked_sub` here.
            nanos -= NANOS_PER_SEC;
        } else {
            debug_assert!(secs > 0);
            debug_assert!(nanos < 0);
            // Never wraps because subtracting +1 from a positive i64 never
            // overflows.
            //
            // MSRV(1.79): Consider using `unchecked_add` here.
            secs -= 1;
            // Never wraps because adding +1_000_000_000 to a negative i32 never overflows.
            //
            // MSRV(1.79): Consider using `unchecked_add` here.
            nanos += NANOS_PER_SEC;
        }
        TimeDelta::new_unchecked(secs, nanos)
    }

    /// Creates a new time delta without handling nanosecond overflow.
    ///
    /// This might produce tighter code in some cases.
    ///
    /// In debug mode only, when `|nanos|` is greater than or equal to 1 second.
    ///
    /// This is not exported so that code outside this module can rely on
    /// `|nanos|` being less than a second for purposes of memory safety.
    pub(super) const fn new_unchecked(secs: i64, nanos: i32) -> TimeDelta {
        debug_assert!(nanos <= 999_999_999);
        debug_assert!(nanos >= -999_999_999);
        TimeDelta { secs, nanos }
    }

    /// Creates a new `TimeDelta` from the given number of whole seconds.
    pub const fn from_secs(secs: i64) -> TimeDelta {
        TimeDelta::new_unchecked(secs, 0)
    }

    /// Creates a new `TimeDelta` from the given number of whole milliseconds.
    ///
    /// Note that since this accepts an `i64`, this method cannot be used
    /// to construct the full range of possible time delta values. In
    /// particular, [`TimeDelta::as_millis`] returns an `i128`, and this
    /// may be a value that would otherwise overflow an `i64`.
    pub const fn from_millis(millis: i64) -> TimeDelta {
        // OK because MILLIS_PER_SEC!={-1,0}.
        let secs = millis / MILLIS_PER_SEC;
        // OK because MILLIS_PER_SEC!={-1,0} and because millis % MILLIS_PER_SEC
        // can be at most 999, and 999 * 1_000_000 never overflows i32.
        let nanos = (millis % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI;
        TimeDelta::new_unchecked(secs, nanos)
    }

    /// Creates a new `TimeDelta` from the given number of whole microseconds.
    ///
    /// Note that since this accepts an `i64`, this method cannot be used
    /// to construct the full range of possible time delta values. In
    /// particular, [`TimeDelta::as_micros`] returns an `i128`, and this
    /// may be a value that would otherwise overflow an `i64`.
    pub const fn from_micros(micros: i64) -> TimeDelta {
        // OK because MICROS_PER_SEC!={-1,0}.
        let secs = micros / MICROS_PER_SEC;
        // OK because MICROS_PER_SEC!={-1,0} and because millis % MICROS_PER_SEC
        // can be at most 999, and 999 * 1_000_000 never overflows i32.
        let nanos = (micros % MICROS_PER_SEC) as i32 * NANOS_PER_MICRO;
        TimeDelta::new_unchecked(secs, nanos)
    }

    /// Creates a new `TimeDelta` from the given number of whole nanoseconds.
    ///
    /// Note that since this accepts an `i64`, this method cannot be used
    /// to construct the full range of possible time delta values. In
    /// particular, [`TimeDelta::as_nanos`] returns an `i128`, which may
    /// be a value that would otherwise overflow an `i64`.
    pub const fn from_nanos(nanos: i64) -> TimeDelta {
        // OK because NANOS_PER_SEC!={-1,0}.
        let secs = nanos / (NANOS_PER_SEC as i64);
        // OK because NANOS_PER_SEC!={-1,0}.
        let nanos = (nanos % (NANOS_PER_SEC as i64)) as i32;
        TimeDelta::new_unchecked(secs, nanos)
    }

    /// Creates a new `TimeDelta` from the given number of hours.
    /// Every hour is exactly `3,600` seconds.
    ///
    /// # Panics
    /// Panics if the number of hours, after being converted to nanoseconds,
    /// overflows the minimum or maximum `TimeDelta` values.
    pub const fn from_hours(hours: i64) -> TimeDelta {
        // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
        const MIN_HOUR: i64 = i64::MIN / (SECS_PER_MINUTE * MINS_PER_HOUR);
        // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
        const MAX_HOUR: i64 = i64::MAX / (SECS_PER_MINUTE * MINS_PER_HOUR);
        // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
        if hours < MIN_HOUR {
            panic!("hours overflowed minimum number of TimeDelta seconds")
        }
        // OK because (SECS_PER_MINUTE*MINS_PER_HOUR)!={-1,0}.
        if hours > MAX_HOUR {
            panic!("hours overflowed maximum number of TimeDelta seconds")
        }
        TimeDelta::from_secs(hours * MINS_PER_HOUR * SECS_PER_MINUTE)
    }

    /// Creates a new `TimeDelta` from the given number of minutes.
    /// Every minute is exactly `60` seconds.
    ///
    /// # Panics
    /// Panics if the number of minutes, after being converted to nanoseconds,
    /// overflows the minimum or maximum `TimeDelta` values.
    pub const fn from_mins(minutes: i64) -> TimeDelta {
        // OK because SECS_PER_MINUTE!={-1,0}.
        const MIN_MINUTE: i64 = i64::MIN / SECS_PER_MINUTE;
        // OK because SECS_PER_MINUTE!={-1,0}.
        const MAX_MINUTE: i64 = i64::MAX / SECS_PER_MINUTE;
        // OK because SECS_PER_MINUTE!={-1,0}.
        if minutes < MIN_MINUTE {
            panic!("minutes overflowed minimum number of TimeDelta seconds")
        }
        // OK because SECS_PER_MINUTE!={-1,0}.
        if minutes > MAX_MINUTE {
            panic!("minutes overflowed maximum number of TimeDelta seconds")
        }
        TimeDelta::from_secs(minutes * SECS_PER_MINUTE)
    }

    /// Returns true if this duration spans no time.
    pub const fn is_zero(&self) -> bool {
        self.secs == 0 && self.nanos == 0
    }

    /// Returns the number of whole seconds in this duration.
    ///
    /// The value returned is negative when the duration is negative.
    ///
    /// This does not include any fractional component corresponding to units
    /// less than a second. To access those, use one of the `subsec` methods
    /// such as [`TimeDelta::subsec_nanos`].
    pub const fn as_secs(&self) -> i64 {
        self.secs
    }

    /// Returns the fractional part of this duration in whole milliseconds.
    ///
    /// The value returned is negative when the duration is negative. It is
    /// guaranteed that the range of the value returned is in the inclusive
    /// range `-999..=999`.
    ///
    /// To get the length of the total duration represented in milliseconds,
    /// use [`TimeDelta::as_millis`].
    pub const fn subsec_millis(&self) -> i32 {
        // OK because NANOS_PER_MILLI!={-1,0}.
        self.nanos / NANOS_PER_MILLI
    }

    /// Returns the fractional part of this duration in whole microseconds.
    ///
    /// The value returned is negative when the duration is negative. It is
    /// guaranteed that the range of the value returned is in the inclusive
    /// range `-999_999..=999_999`.
    ///
    /// To get the length of the total duration represented in microseconds,
    /// use [`TimeDelta::as_micros`].
    pub const fn subsec_micros(&self) -> i32 {
        // OK because NANOS_PER_MICRO!={-1,0}.
        self.nanos / NANOS_PER_MICRO
    }

    /// Returns the fractional part of this duration in whole nanoseconds.
    ///
    /// The value returned is negative when the duration is negative. It is
    /// guaranteed that the range of the value returned is in the inclusive
    /// range `-999_999_999..=999_999_999`.
    ///
    /// To get the length of the total duration represented in nanoseconds,
    /// use [`TimeDelta::as_nanos`].
    pub const fn subsec_nanos(&self) -> i32 {
        self.nanos
    }

    /// Returns the total duration in units of whole milliseconds.
    ///
    /// The value returned is negative when the duration is negative.
    ///
    /// To get only the fractional component of this duration in units of
    /// whole milliseconds, use [`TimeDelta::subsec_millis`].
    pub const fn as_millis(&self) -> i128 {
        // OK because 1_000 times any i64 will never overflow i128.
        let millis = (self.secs as i128) * (MILLIS_PER_SEC as i128);
        // OK because NANOS_PER_MILLI!={-1,0}.
        let subsec_millis = (self.nanos / NANOS_PER_MILLI) as i128;
        // OK because subsec_millis maxes out at 999, and adding that to
        // i64::MAX*1_000 will never overflow a i128.
        millis + subsec_millis
    }

    /// Returns the total duration in units of whole microseconds.
    ///
    /// The value returned is negative when the duration is negative.
    ///
    /// To get only the fractional component of this duration in units of
    /// whole microseconds, use [`TimeDelta::subsec_micros`].
    pub const fn as_micros(&self) -> i128 {
        // OK because 1_000_000 times any i64 will never overflow i128.
        let micros = (self.secs as i128) * (MICROS_PER_SEC as i128);
        // OK because NANOS_PER_MICRO!={-1,0}.
        let subsec_micros = (self.nanos / NANOS_PER_MICRO) as i128;
        // OK because subsec_micros maxes out at 999_999, and adding that to
        // i64::MAX*1_000_000 will never overflow a i128.
        micros + subsec_micros
    }

    /// Returns the total duration in units of whole nanoseconds.
    ///
    /// The value returned is negative when the duration is negative.
    ///
    /// To get only the fractional component of this duration in units of
    /// whole nanoseconds, use [`TimeDelta::subsec_nanos`].
    pub const fn as_nanos(&self) -> i128 {
        // OK because 1_000_000_000 times any i64 will never overflow i128.
        let nanos = (self.secs as i128) * (NANOS_PER_SEC as i128);
        // OK because subsec_nanos maxes out at 999_999_999, and adding that to
        // i64::MAX*1_000_000_000 will never overflow a i128.
        nanos + (self.nanos as i128)
    }

    // NOTE: We don't provide `abs_diff` here because we can't represent the
    // difference between all possible durations. For example,
    // `abs_diff(TimeDelta::MAX, TimeDelta::MIN)`. It therefore seems
    // like we should actually return a `std::time::Duration` here, but I'm
    // trying to be conservative when diverging from std.
}

/// Additional methods not found in the standard library.
///
/// In most cases, these APIs exist as a result of the fact that this duration is signed.
#[rustfmt::skip]
impl TimeDelta {
    /// Returns the number of whole hours in this duration.
    ///
    /// The value returned is negative when the duration is negative.
    ///
    /// This does not include any fractional component corresponding to units
    /// less than an hour.
    pub const fn as_hours(&self) -> i64 { self.as_secs() / (MINS_PER_HOUR * SECS_PER_MINUTE) }

    /// Returns the number of whole minutes in this duration.
    ///
    /// The value returned is negative when the duration is negative.
    ///
    /// This does not include any fractional component corresponding to units less than a minute.
    pub const fn as_mins(&self) -> i64 { self.as_secs() / SECS_PER_MINUTE }

    /// Returns the absolute value of this time delta.
    ///
    /// If this duration is positive, then this returns the original duration unchanged.
    ///
    /// # Panics
    /// This panics when the seconds component of this time delta is equal to `i64::MIN`.
    pub const fn abs(self) -> TimeDelta {
        TimeDelta::new_unchecked(self.secs.abs(), self.nanos.abs())
    }

    /// Returns the negative absolute value of this time delta.
    ///
    /// If this duration is negative, then this returns the original duration unchanged.
    ///
    /// # Panics
    /// This panics when the seconds component of this time delta is equal to `i64::MIN`.
    pub const fn neg_abs(self) -> TimeDelta {
        TimeDelta::new_unchecked(-self.secs.abs(), -self.nanos.abs())
    }

    /// Returns this duration with its sign flipped.
    ///
    /// If this duration is zero, then this returns the duration unchanged.
    ///
    /// This returns none if the negation does not exist. This occurs in
    /// precisely the cases when [`TimeDelta::as_secs`] is equal to `i64::MIN`.
    pub const fn checked_neg(self) -> Option<TimeDelta> {
        let Some(secs) = self.secs.checked_neg() else { return None; };
        // -self.nanos always OK because `-999_999_999 <= self.nanos <= 999_999_999`.
        Some(TimeDelta::new_unchecked(secs, -self.nanos))
    }

    /// Returns a number that represents the sign of this duration.
    ///
    /// * When [`TimeDelta::is_zero`] is true, this returns `0`.
    /// * When [`TimeDelta::is_positive`] is true, this returns `1`.
    /// * When [`TimeDelta::is_negative`] is true, this returns `-1`.
    ///
    /// The above cases are mutually exclusive.
    pub const fn signum(self) -> i8 {
        if self.is_zero() { 0 }
        else if self.is_positive() { 1 }
        else { debug_assert!(self.is_negative()); -1 }
    }

    /// Returns true when this duration is positive. That is, greater than [`TimeDelta::ZERO`].
    pub const fn is_positive(&self) -> bool { self.secs.is_positive() || self.nanos.is_positive() }
    /// Returns true when this duration is negative. That is, less than [`TimeDelta::ZERO`].
    pub const fn is_negative(&self) -> bool { self.secs.is_negative() || self.nanos.is_negative() }
}

#[cfg(feature = "std")]
mod impl_system_instant {
    use crate::{Add, Duration, Sub, SystemInstant, TimeDelta, unwrap};

    /// Additional APIs involving `SystemInstant`.
    impl TimeDelta {
        /// Adds a `TimeDelta` to an instant, moving forward or backward in time.
        ///
        /// # Panics
        /// Panics if the result is outside the valid range of `SystemInstant`.
        pub fn after(&self, instant: SystemInstant) -> SystemInstant {
            if self.is_positive() {
                instant + Duration::new(self.secs as u64, self.nanos as u32)
            } else {
                instant - Duration::new((-self.secs) as u64, (-self.nanos) as u32)
            }
        }

        /// Adds a `TimeDelta` to an instant
        ///
        /// Returns `None` if the result is outside the valid range.
        pub fn checked_after(&self, instant: SystemInstant) -> Option<SystemInstant> {
            if self.is_positive() {
                instant.checked_add(Duration::new(self.secs as u64, self.nanos as u32))
            } else {
                let secs = unwrap![some? self.secs.checked_neg()] as u64;
                let nanos = unwrap![some? self.nanos.checked_neg()] as u32;
                instant.checked_sub(Duration::new(secs, nanos))
            }
        }
    }

    /// Shifts Instant forward or backward.
    impl Add<TimeDelta> for SystemInstant {
        type Output = SystemInstant;
        fn add(self, rhs: TimeDelta) -> SystemInstant {
            if rhs.is_positive() {
                self + Duration::new(rhs.secs as u64, rhs.nanos as u32)
            } else {
                self - Duration::new((-rhs.secs) as u64, (-rhs.nanos) as u32)
            }
        }
    }
    /// Moves Instant backward or forward.
    impl Sub<TimeDelta> for SystemInstant {
        type Output = SystemInstant;
        fn sub(self, rhs: TimeDelta) -> SystemInstant {
            if rhs.is_positive() {
                self - Duration::new(rhs.secs as u64, rhs.nanos as u32)
            } else {
                self + Duration::new((-rhs.secs) as u64, (-rhs.nanos) as u32)
            }
        }
    }
    /// Returns a signed TimeDelta.
    impl Sub<SystemInstant> for TimeDelta {
        type Output = SystemInstant;
        fn sub(self, rhs: SystemInstant) -> SystemInstant {
            if self.is_positive() {
                rhs - Duration::new(self.secs as u64, self.nanos as u32)
            } else {
                rhs + Duration::new((-self.secs) as u64, (-self.nanos) as u32)
            }
        }
    }
}