Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement checked_add_duration for SystemTime #55527

Merged
merged 3 commits into from
Nov 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/libstd/sys/cloudabi/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ pub struct Instant {
t: abi::timestamp,
}

pub fn dur2intervals(dur: &Duration) -> abi::timestamp {
fn checked_dur2intervals(dur: &Duration) -> Option<abi::timestamp> {
dur.as_secs()
.checked_mul(NSEC_PER_SEC)
.and_then(|nanos| nanos.checked_add(dur.subsec_nanos() as abi::timestamp))
}

pub fn dur2intervals(dur: &Duration) -> abi::timestamp {
checked_dur2intervals(dur)
.expect("overflow converting duration to nanoseconds")
}

Expand Down Expand Up @@ -92,11 +96,14 @@ impl SystemTime {
}

pub fn add_duration(&self, other: &Duration) -> SystemTime {
SystemTime {
t: self.t
.checked_add(dur2intervals(other))
.expect("overflow when adding duration to instant"),
}
self.checked_add_duration(other)
.expect("overflow when adding duration to instant")
}

pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
checked_dur2intervals(other)
.and_then(|d| self.t.checked_add(d))
.map(|t| SystemTime {t})
}

pub fn sub_duration(&self, other: &Duration) -> SystemTime {
Expand Down
18 changes: 12 additions & 6 deletions src/libstd/sys/redox/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,29 @@ impl Timespec {
}

fn add_duration(&self, other: &Duration) -> Timespec {
self.checked_add_duration(other).expect("overflow when adding duration to time")
}

fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `i64`
.ok()
.and_then(|secs| self.t.tv_sec.checked_add(secs))
.expect("overflow when adding duration to time");
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;

// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1).expect("overflow when adding \
duration to time");
secs = secs.checked_add(1)?;
}
Timespec {
Some(Timespec {
t: syscall::TimeSpec {
tv_sec: secs,
tv_nsec: nsec as i32,
},
}
})
}

fn sub_duration(&self, other: &Duration) -> Timespec {
Expand Down Expand Up @@ -180,6 +182,10 @@ impl SystemTime {
SystemTime { t: self.t.add_duration(other) }
}

pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
self.t.checked_add_duration(other).map(|t| SystemTime { t })
}

pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime { t: self.t.sub_duration(other) }
}
Expand Down
22 changes: 16 additions & 6 deletions src/libstd/sys/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,29 @@ impl Timespec {
}

fn add_duration(&self, other: &Duration) -> Timespec {
self.checked_add_duration(other).expect("overflow when adding duration to time")
}

fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = other
.as_secs()
.try_into() // <- target type would be `libc::time_t`
.ok()
.and_then(|secs| self.t.tv_sec.checked_add(secs))
.expect("overflow when adding duration to time");
.and_then(|secs| self.t.tv_sec.checked_add(secs))?;

// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1).expect("overflow when adding \
duration to time");
secs = secs.checked_add(1)?;
}
Timespec {
Some(Timespec {
t: libc::timespec {
tv_sec: secs,
tv_nsec: nsec as _,
},
}
})
}

fn sub_duration(&self, other: &Duration) -> Timespec {
Expand Down Expand Up @@ -201,6 +203,10 @@ mod inner {
SystemTime { t: self.t.add_duration(other) }
}

pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
self.t.checked_add_duration(other).map(|t| SystemTime { t })
}

pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime { t: self.t.sub_duration(other) }
}
Expand Down Expand Up @@ -325,6 +331,10 @@ mod inner {
SystemTime { t: self.t.add_duration(other) }
}

pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
self.t.checked_add_duration(other).map(|t| SystemTime { t })
}

pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime { t: self.t.sub_duration(other) }
}
Expand Down
4 changes: 4 additions & 0 deletions src/libstd/sys/wasm/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ impl SystemTime {
SystemTime(self.0 + *other)
}

pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
self.0.checked_add(*other).map(|d| SystemTime(d))
}

pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime(self.0 - *other)
}
Expand Down
16 changes: 12 additions & 4 deletions src/libstd/sys/windows/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,13 @@ impl SystemTime {
}

pub fn add_duration(&self, other: &Duration) -> SystemTime {
let intervals = self.intervals().checked_add(dur2intervals(other))
.expect("overflow when adding duration to time");
SystemTime::from_intervals(intervals)
self.checked_add_duration(other).expect("overflow when adding duration to time")
}

pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
checked_dur2intervals(other)
.and_then(|d| self.intervals().checked_add(d))
.map(|i| SystemTime::from_intervals(i))
}

pub fn sub_duration(&self, other: &Duration) -> SystemTime {
Expand Down Expand Up @@ -180,11 +184,15 @@ impl Hash for SystemTime {
}
}

fn dur2intervals(d: &Duration) -> i64 {
fn checked_dur2intervals(d: &Duration) -> Option<i64> {
d.as_secs()
.checked_mul(INTERVALS_PER_SEC)
.and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100))
.and_then(|i| i.try_into().ok())
}

fn dur2intervals(d: &Duration) -> i64 {
checked_dur2intervals(d)
.expect("overflow when converting duration to intervals")
}

Expand Down
21 changes: 21 additions & 0 deletions src/libstd/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,14 @@ impl SystemTime {
pub fn elapsed(&self) -> Result<Duration, SystemTimeError> {
SystemTime::now().duration_since(*self)
}

/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
#[unstable(feature = "time_checked_add", issue = "55940")]
pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
self.0.checked_add_duration(&duration).map(|t| SystemTime(t))
}
}

#[stable(feature = "time2", since = "1.8.0")]
Expand Down Expand Up @@ -557,6 +565,19 @@ mod tests {
let one_second_from_epoch2 = UNIX_EPOCH + Duration::new(0, 500_000_000)
+ Duration::new(0, 500_000_000);
assert_eq!(one_second_from_epoch, one_second_from_epoch2);

// checked_add_duration will not panic on overflow
let mut maybe_t = Some(SystemTime::UNIX_EPOCH);
let max_duration = Duration::from_secs(u64::max_value());
// in case `SystemTime` can store `>= UNIX_EPOCH + max_duration`.
for _ in 0..2 {
maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration));
}
assert_eq!(maybe_t, None);

// checked_add_duration calculates the right time and will work for another year
let year = Duration::from_secs(60 * 60 * 24 * 365);
assert_eq!(a + year, a.checked_add(year).unwrap());
}

#[test]
Expand Down