Skip to content

Commit 5def753

Browse files
committed
Fix checked_{add,sub}_duration incorrectly returning None when other has more than i64::MAX seconds
1 parent 9b0a099 commit 5def753

File tree

4 files changed

+35
-24
lines changed

4 files changed

+35
-24
lines changed

library/std/src/sys/hermit/time.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,7 @@ impl Timespec {
3939
}
4040

4141
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
42-
let mut secs = other
43-
.as_secs()
44-
.try_into() // <- target type would be `libc::time_t`
45-
.ok()
46-
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;
42+
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
4743

4844
// Nano calculations can't overflow because nanos are <1B which fit
4945
// in a u32.
@@ -56,11 +52,7 @@ impl Timespec {
5652
}
5753

5854
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
59-
let mut secs = other
60-
.as_secs()
61-
.try_into() // <- target type would be `libc::time_t`
62-
.ok()
63-
.and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
55+
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
6456

6557
// Similar to above, nanos can't overflow.
6658
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;

library/std/src/sys/solid/time.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ impl SystemTime {
4747
}
4848

4949
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
50-
Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
50+
Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?))
5151
}
5252

5353
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
54-
Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
54+
Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?))
5555
}
5656
}

library/std/src/sys/unix/time.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,7 @@ impl Timespec {
102102
}
103103

104104
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
105-
let mut secs = other
106-
.as_secs()
107-
.try_into() // <- target type would be `i64`
108-
.ok()
109-
.and_then(|secs| self.tv_sec.checked_add(secs))?;
105+
let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
110106

111107
// Nano calculations can't overflow because nanos are <1B which fit
112108
// in a u32.
@@ -115,23 +111,19 @@ impl Timespec {
115111
nsec -= NSEC_PER_SEC as u32;
116112
secs = secs.checked_add(1)?;
117113
}
118-
Some(Timespec::new(secs, nsec as i64))
114+
Some(Timespec::new(secs, nsec.into()))
119115
}
120116

121117
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
122-
let mut secs = other
123-
.as_secs()
124-
.try_into() // <- target type would be `i64`
125-
.ok()
126-
.and_then(|secs| self.tv_sec.checked_sub(secs))?;
118+
let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
127119

128120
// Similar to above, nanos can't overflow.
129121
let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
130122
if nsec < 0 {
131123
nsec += NSEC_PER_SEC as i32;
132124
secs = secs.checked_sub(1)?;
133125
}
134-
Some(Timespec::new(secs, nsec as i64))
126+
Some(Timespec::new(secs, nsec.into()))
135127
}
136128

137129
#[allow(dead_code)]

library/std/src/time/tests.rs

+27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
2+
use core::fmt::Debug;
23
#[cfg(not(target_arch = "wasm32"))]
34
use test::{black_box, Bencher};
45

@@ -193,6 +194,32 @@ fn since_epoch() {
193194
assert!(a < hundred_twenty_years);
194195
}
195196

197+
#[test]
198+
fn big_math() {
199+
// Check that the same result occurs when adding/subtracting each duration one at a time as when
200+
// adding/subtracting them all at once.
201+
#[track_caller]
202+
fn check<T: Eq + Copy + Debug>(start: Option<T>, op: impl Fn(&T, Duration) -> Option<T>) {
203+
const DURATIONS: [Duration; 2] =
204+
[Duration::from_secs(i64::MAX as _), Duration::from_secs(50)];
205+
if let Some(start) = start {
206+
assert_eq!(
207+
op(&start, DURATIONS.into_iter().sum()),
208+
DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d))
209+
)
210+
}
211+
}
212+
213+
check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add);
214+
check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub);
215+
216+
let instant = Instant::now();
217+
check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add);
218+
check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add);
219+
check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub);
220+
check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub);
221+
}
222+
196223
macro_rules! bench_instant_threaded {
197224
($bench_name:ident, $thread_count:expr) => {
198225
#[bench]

0 commit comments

Comments
 (0)