Skip to content

Commit ec08128

Browse files
authored
Rollup merge of #36463 - eugene-bulkin:duration-checked-ops, r=alexcrichton
Add checked operation methods to Duration Addresses #35774.
2 parents 0c9dc53 + b6321bd commit ec08128

File tree

1 file changed

+182
-35
lines changed

1 file changed

+182
-35
lines changed

src/libstd/time/duration.rs

+182-35
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,154 @@ impl Duration {
9797
#[stable(feature = "duration", since = "1.3.0")]
9898
#[inline]
9999
pub fn subsec_nanos(&self) -> u32 { self.nanos }
100+
101+
/// Checked duration addition. Computes `self + other`, returning `None`
102+
/// if overflow occurred.
103+
///
104+
/// # Examples
105+
///
106+
/// Basic usage:
107+
///
108+
/// ```
109+
/// #![feature(duration_checked_ops)]
110+
///
111+
/// use std::time::Duration;
112+
///
113+
/// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1)));
114+
/// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(std::u64::MAX, 0)), None);
115+
/// ```
116+
#[unstable(feature = "duration_checked_ops", issue = "35774")]
117+
#[inline]
118+
pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
119+
if let Some(mut secs) = self.secs.checked_add(rhs.secs) {
120+
let mut nanos = self.nanos + rhs.nanos;
121+
if nanos >= NANOS_PER_SEC {
122+
nanos -= NANOS_PER_SEC;
123+
if let Some(new_secs) = secs.checked_add(1) {
124+
secs = new_secs;
125+
} else {
126+
return None;
127+
}
128+
}
129+
debug_assert!(nanos < NANOS_PER_SEC);
130+
Some(Duration {
131+
secs: secs,
132+
nanos: nanos,
133+
})
134+
} else {
135+
None
136+
}
137+
}
138+
139+
/// Checked duration subtraction. Computes `self + other`, returning `None`
140+
/// if the result would be negative or if underflow occurred.
141+
///
142+
/// # Examples
143+
///
144+
/// Basic usage:
145+
///
146+
/// ```
147+
/// #![feature(duration_checked_ops)]
148+
///
149+
/// use std::time::Duration;
150+
///
151+
/// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1)));
152+
/// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None);
153+
/// ```
154+
#[unstable(feature = "duration_checked_ops", issue = "35774")]
155+
#[inline]
156+
pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
157+
if let Some(mut secs) = self.secs.checked_sub(rhs.secs) {
158+
let nanos = if self.nanos >= rhs.nanos {
159+
self.nanos - rhs.nanos
160+
} else {
161+
if let Some(sub_secs) = secs.checked_sub(1) {
162+
secs = sub_secs;
163+
self.nanos + NANOS_PER_SEC - rhs.nanos
164+
} else {
165+
return None;
166+
}
167+
};
168+
debug_assert!(nanos < NANOS_PER_SEC);
169+
Some(Duration { secs: secs, nanos: nanos })
170+
} else {
171+
None
172+
}
173+
}
174+
175+
/// Checked duration multiplication. Computes `self * other`, returning
176+
/// `None` if underflow or overflow occurred.
177+
///
178+
/// # Examples
179+
///
180+
/// Basic usage:
181+
///
182+
/// ```
183+
/// #![feature(duration_checked_ops)]
184+
///
185+
/// use std::time::Duration;
186+
///
187+
/// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2)));
188+
/// assert_eq!(Duration::new(std::u64::MAX - 1, 0).checked_mul(2), None);
189+
/// ```
190+
#[unstable(feature = "duration_checked_ops", issue = "35774")]
191+
#[inline]
192+
pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
193+
// Multiply nanoseconds as u64, because it cannot overflow that way.
194+
let total_nanos = self.nanos as u64 * rhs as u64;
195+
let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
196+
let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
197+
if let Some(secs) = self.secs
198+
.checked_mul(rhs as u64)
199+
.and_then(|s| s.checked_add(extra_secs)) {
200+
debug_assert!(nanos < NANOS_PER_SEC);
201+
Some(Duration {
202+
secs: secs,
203+
nanos: nanos,
204+
})
205+
} else {
206+
None
207+
}
208+
}
209+
210+
/// Checked duration division. Computes `self / other`, returning `None`
211+
/// if `other == 0` or the operation results in underflow or overflow.
212+
///
213+
/// # Examples
214+
///
215+
/// Basic usage:
216+
///
217+
/// ```
218+
/// #![feature(duration_checked_ops)]
219+
///
220+
/// use std::time::Duration;
221+
///
222+
/// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
223+
/// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000)));
224+
/// assert_eq!(Duration::new(2, 0).checked_div(0), None);
225+
/// ```
226+
#[unstable(feature = "duration_checked_ops", issue = "35774")]
227+
#[inline]
228+
pub fn checked_div(self, rhs: u32) -> Option<Duration> {
229+
if rhs != 0 {
230+
let secs = self.secs / (rhs as u64);
231+
let carry = self.secs - secs * (rhs as u64);
232+
let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64);
233+
let nanos = self.nanos / rhs + (extra_nanos as u32);
234+
debug_assert!(nanos < NANOS_PER_SEC);
235+
Some(Duration { secs: secs, nanos: nanos })
236+
} else {
237+
None
238+
}
239+
}
100240
}
101241

102242
#[stable(feature = "duration", since = "1.3.0")]
103243
impl Add for Duration {
104244
type Output = Duration;
105245

106246
fn add(self, rhs: Duration) -> Duration {
107-
let mut secs = self.secs.checked_add(rhs.secs)
108-
.expect("overflow when adding durations");
109-
let mut nanos = self.nanos + rhs.nanos;
110-
if nanos >= NANOS_PER_SEC {
111-
nanos -= NANOS_PER_SEC;
112-
secs = secs.checked_add(1).expect("overflow when adding durations");
113-
}
114-
debug_assert!(nanos < NANOS_PER_SEC);
115-
Duration { secs: secs, nanos: nanos }
247+
self.checked_add(rhs).expect("overflow when adding durations")
116248
}
117249
}
118250

@@ -128,17 +260,7 @@ impl Sub for Duration {
128260
type Output = Duration;
129261

130262
fn sub(self, rhs: Duration) -> Duration {
131-
let mut secs = self.secs.checked_sub(rhs.secs)
132-
.expect("overflow when subtracting durations");
133-
let nanos = if self.nanos >= rhs.nanos {
134-
self.nanos - rhs.nanos
135-
} else {
136-
secs = secs.checked_sub(1)
137-
.expect("overflow when subtracting durations");
138-
self.nanos + NANOS_PER_SEC - rhs.nanos
139-
};
140-
debug_assert!(nanos < NANOS_PER_SEC);
141-
Duration { secs: secs, nanos: nanos }
263+
self.checked_sub(rhs).expect("overflow when subtracting durations")
142264
}
143265
}
144266

@@ -154,15 +276,7 @@ impl Mul<u32> for Duration {
154276
type Output = Duration;
155277

156278
fn mul(self, rhs: u32) -> Duration {
157-
// Multiply nanoseconds as u64, because it cannot overflow that way.
158-
let total_nanos = self.nanos as u64 * rhs as u64;
159-
let extra_secs = total_nanos / (NANOS_PER_SEC as u64);
160-
let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32;
161-
let secs = self.secs.checked_mul(rhs as u64)
162-
.and_then(|s| s.checked_add(extra_secs))
163-
.expect("overflow when multiplying duration");
164-
debug_assert!(nanos < NANOS_PER_SEC);
165-
Duration { secs: secs, nanos: nanos }
279+
self.checked_mul(rhs).expect("overflow when multiplying duration by scalar")
166280
}
167281
}
168282

@@ -178,12 +292,7 @@ impl Div<u32> for Duration {
178292
type Output = Duration;
179293

180294
fn div(self, rhs: u32) -> Duration {
181-
let secs = self.secs / (rhs as u64);
182-
let carry = self.secs - secs * (rhs as u64);
183-
let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64);
184-
let nanos = self.nanos / rhs + (extra_nanos as u32);
185-
debug_assert!(nanos < NANOS_PER_SEC);
186-
Duration { secs: secs, nanos: nanos }
295+
self.checked_div(rhs).expect("divide by zero error when dividing duration by scalar")
187296
}
188297
}
189298

@@ -234,6 +343,15 @@ mod tests {
234343
Duration::new(1, 1));
235344
}
236345

346+
#[test]
347+
fn checked_add() {
348+
assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)),
349+
Some(Duration::new(0, 1)));
350+
assert_eq!(Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)),
351+
Some(Duration::new(1, 1)));
352+
assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None);
353+
}
354+
237355
#[test]
238356
fn sub() {
239357
assert_eq!(Duration::new(0, 1) - Duration::new(0, 0),
@@ -244,6 +362,18 @@ mod tests {
244362
Duration::new(0, 999_999_999));
245363
}
246364

365+
#[test]
366+
fn checked_sub() {
367+
let zero = Duration::new(0, 0);
368+
let one_nano = Duration::new(0, 1);
369+
let one_sec = Duration::new(1, 0);
370+
assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1)));
371+
assert_eq!(one_sec.checked_sub(one_nano),
372+
Some(Duration::new(0, 999_999_999)));
373+
assert_eq!(zero.checked_sub(one_nano), None);
374+
assert_eq!(zero.checked_sub(one_sec), None);
375+
}
376+
247377
#[test] #[should_panic]
248378
fn sub_bad1() {
249379
Duration::new(0, 0) - Duration::new(0, 1);
@@ -263,11 +393,28 @@ mod tests {
263393
Duration::new(2000, 4000));
264394
}
265395

396+
#[test]
397+
fn checked_mul() {
398+
assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2)));
399+
assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3)));
400+
assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4)));
401+
assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000),
402+
Some(Duration::new(2000, 4000)));
403+
assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None);
404+
}
405+
266406
#[test]
267407
fn div() {
268408
assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));
269409
assert_eq!(Duration::new(1, 1) / 3, Duration::new(0, 333_333_333));
270410
assert_eq!(Duration::new(99, 999_999_000) / 100,
271411
Duration::new(0, 999_999_990));
272412
}
413+
414+
#[test]
415+
fn checked_div() {
416+
assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
417+
assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000)));
418+
assert_eq!(Duration::new(2, 0).checked_div(0), None);
419+
}
273420
}

0 commit comments

Comments
 (0)