Skip to content

Commit 606cded

Browse files
author
Eugene Bulkin
committed
Add checked operation methods to Duration
1 parent 1fca1ab commit 606cded

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

src/libstd/time/duration.rs

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

102226
#[stable(feature = "duration", since = "1.3.0")]
@@ -234,6 +358,15 @@ mod tests {
234358
Duration::new(1, 1));
235359
}
236360

361+
#[test]
362+
fn checked_add() {
363+
assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)),
364+
Some(Duration::new(0, 1)));
365+
assert_eq!(Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)),
366+
Some(Duration::new(1, 1)));
367+
assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None);
368+
}
369+
237370
#[test]
238371
fn sub() {
239372
assert_eq!(Duration::new(0, 1) - Duration::new(0, 0),
@@ -244,6 +377,18 @@ mod tests {
244377
Duration::new(0, 999_999_999));
245378
}
246379

380+
#[test]
381+
fn checked_sub() {
382+
let zero = Duration::new(0, 0);
383+
let one_nano = Duration::new(0, 1);
384+
let one_sec = Duration::new(1, 0);
385+
assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1)));
386+
assert_eq!(one_sec.checked_sub(one_nano),
387+
Some(Duration::new(0, 999_999_999)));
388+
assert_eq!(zero.checked_sub(one_nano), None);
389+
assert_eq!(zero.checked_sub(one_sec), None);
390+
}
391+
247392
#[test] #[should_panic]
248393
fn sub_bad1() {
249394
Duration::new(0, 0) - Duration::new(0, 1);
@@ -263,11 +408,28 @@ mod tests {
263408
Duration::new(2000, 4000));
264409
}
265410

411+
#[test]
412+
fn checked_mul() {
413+
assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2)));
414+
assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3)));
415+
assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4)));
416+
assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000),
417+
Some(Duration::new(2000, 4000)));
418+
assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None);
419+
}
420+
266421
#[test]
267422
fn div() {
268423
assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));
269424
assert_eq!(Duration::new(1, 1) / 3, Duration::new(0, 333_333_333));
270425
assert_eq!(Duration::new(99, 999_999_000) / 100,
271426
Duration::new(0, 999_999_990));
272427
}
428+
429+
#[test]
430+
fn checked_div() {
431+
assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0)));
432+
assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000)));
433+
assert_eq!(Duration::new(2, 0).checked_div(0), None);
434+
}
273435
}

0 commit comments

Comments
 (0)