From b869aa5f316ae065ce2215e69811e3216c6250cb Mon Sep 17 00:00:00 2001
From: marmeladema <xademax@gmail.com>
Date: Sun, 30 Aug 2020 18:31:34 +0100
Subject: [PATCH] Add saturating methods for `Duration`

---
 library/core/src/lib.rs                 |  1 +
 library/core/src/time.rs                | 97 +++++++++++++++++++++++++
 library/core/tests/lib.rs               |  2 +
 library/core/tests/time.rs              | 30 ++++++++
 src/test/ui/consts/duration-consts-2.rs | 42 +++++++----
 5 files changed, 156 insertions(+), 16 deletions(-)

diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 29bbf062f22e4..c3cadcbb01e31 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -100,6 +100,7 @@
 #![feature(doc_cfg)]
 #![feature(doc_spotlight)]
 #![feature(duration_consts_2)]
+#![feature(duration_saturating_ops)]
 #![feature(extern_types)]
 #![feature(fundamental)]
 #![feature(intrinsics)]
diff --git a/library/core/src/time.rs b/library/core/src/time.rs
index 5741f8a53b522..f39781788d7c0 100644
--- a/library/core/src/time.rs
+++ b/library/core/src/time.rs
@@ -108,6 +108,34 @@ impl Duration {
     #[unstable(feature = "duration_constants", issue = "57391")]
     pub const NANOSECOND: Duration = Duration::from_nanos(1);
 
+    /// The minimum duration.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::MIN, Duration::new(0, 0));
+    /// ```
+    #[unstable(feature = "duration_constants", issue = "57391")]
+    pub const MIN: Duration = Duration::from_nanos(0);
+
+    /// The maximum duration.
+    ///
+    /// It is roughly equal to a duration of 584,942,417,355 years.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1));
+    /// ```
+    #[unstable(feature = "duration_constants", issue = "57391")]
+    pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1);
+
     /// Creates a new `Duration` from the specified number of whole seconds and
     /// additional nanoseconds.
     ///
@@ -450,6 +478,29 @@ impl Duration {
         }
     }
 
+    /// Saturating `Duration` addition. Computes `self + other`, returning [`Duration::MAX`]
+    /// if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_saturating_ops)]
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
+    /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
+    /// ```
+    #[unstable(feature = "duration_saturating_ops", issue = "76416")]
+    #[inline]
+    #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
+    pub const fn saturating_add(self, rhs: Duration) -> Duration {
+        match self.checked_add(rhs) {
+            Some(res) => res,
+            None => Duration::MAX,
+        }
+    }
+
     /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`]
     /// if the result would be negative or if overflow occurred.
     ///
@@ -485,6 +536,29 @@ impl Duration {
         }
     }
 
+    /// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`]
+    /// if the result would be negative or if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_saturating_ops)]
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1));
+    /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN);
+    /// ```
+    #[unstable(feature = "duration_saturating_ops", issue = "76416")]
+    #[inline]
+    #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
+    pub const fn saturating_sub(self, rhs: Duration) -> Duration {
+        match self.checked_sub(rhs) {
+            Some(res) => res,
+            None => Duration::MIN,
+        }
+    }
+
     /// Checked `Duration` multiplication. Computes `self * other`, returning
     /// [`None`] if overflow occurred.
     ///
@@ -515,6 +589,29 @@ impl Duration {
         None
     }
 
+    /// Saturating `Duration` multiplication. Computes `self * other`, returning
+    /// [`Duration::MAX`] if overflow occurred.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(duration_saturating_ops)]
+    /// #![feature(duration_constants)]
+    /// use std::time::Duration;
+    ///
+    /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2));
+    /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
+    /// ```
+    #[unstable(feature = "duration_saturating_ops", issue = "76416")]
+    #[inline]
+    #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")]
+    pub const fn saturating_mul(self, rhs: u32) -> Duration {
+        match self.checked_mul(rhs) {
+            Some(res) => res,
+            None => Duration::MAX,
+        }
+    }
+
     /// Checked `Duration` division. Computes `self / other`, returning [`None`]
     /// if `other == 0`.
     ///
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 4a651e5aa0ee3..a2e294ace1860 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -10,6 +10,8 @@
 #![feature(core_private_diy_float)]
 #![feature(debug_non_exhaustive)]
 #![feature(dec2flt)]
+#![feature(duration_constants)]
+#![feature(duration_saturating_ops)]
 #![feature(exact_size_is_empty)]
 #![feature(fixed_size_array)]
 #![feature(flt2dec)]
diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs
index 7a6675dc82fa6..4f90eb63b0472 100644
--- a/library/core/tests/time.rs
+++ b/library/core/tests/time.rs
@@ -89,6 +89,16 @@ fn checked_add() {
     assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None);
 }
 
+#[test]
+fn saturating_add() {
+    assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1));
+    assert_eq!(
+        Duration::new(0, 500_000_000).saturating_add(Duration::new(0, 500_000_001)),
+        Duration::new(1, 1)
+    );
+    assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX);
+}
+
 #[test]
 fn sub() {
     assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1));
@@ -107,6 +117,17 @@ fn checked_sub() {
     assert_eq!(zero.checked_sub(one_sec), None);
 }
 
+#[test]
+fn saturating_sub() {
+    let zero = Duration::new(0, 0);
+    let one_nano = Duration::new(0, 1);
+    let one_sec = Duration::new(1, 0);
+    assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1));
+    assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999));
+    assert_eq!(zero.saturating_sub(one_nano), Duration::MIN);
+    assert_eq!(zero.saturating_sub(one_sec), Duration::MIN);
+}
+
 #[test]
 #[should_panic]
 fn sub_bad1() {
@@ -136,6 +157,15 @@ fn checked_mul() {
     assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None);
 }
 
+#[test]
+fn saturating_mul() {
+    assert_eq!(Duration::new(0, 1).saturating_mul(2), Duration::new(0, 2));
+    assert_eq!(Duration::new(1, 1).saturating_mul(3), Duration::new(3, 3));
+    assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4), Duration::new(2, 4));
+    assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4000), Duration::new(2000, 4000));
+    assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX);
+}
+
 #[test]
 fn div() {
     assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0));
diff --git a/src/test/ui/consts/duration-consts-2.rs b/src/test/ui/consts/duration-consts-2.rs
index c8b3939933126..bc0969e4f1fba 100644
--- a/src/test/ui/consts/duration-consts-2.rs
+++ b/src/test/ui/consts/duration-consts-2.rs
@@ -3,6 +3,7 @@
 #![feature(const_panic)]
 #![feature(duration_consts_2)]
 #![feature(div_duration)]
+#![feature(duration_saturating_ops)]
 
 use std::time::Duration;
 
@@ -15,29 +16,29 @@ fn duration() {
 
     const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
 
-    const MAX_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
-    assert_eq!(MAX_ADD_ZERO, Some(MAX));
+    const MAX_CHECKED_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
+    assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX));
 
-    const MAX_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
-    assert_eq!(MAX_ADD_ONE, None);
+    const MAX_CHECKED_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
+    assert_eq!(MAX_CHECKED_ADD_ONE, None);
 
-    const ONE_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
-    assert_eq!(ONE_SUB_ONE, Some(ZERO));
+    const ONE_CHECKED_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
+    assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO));
 
-    const ZERO_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
-    assert_eq!(ZERO_SUB_ONE, None);
+    const ZERO_CHECKED_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
+    assert_eq!(ZERO_CHECKED_SUB_ONE, None);
 
-    const ONE_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
-    assert_eq!(ONE_MUL_ONE, Some(ONE));
+    const ONE_CHECKED_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
+    assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE));
 
-    const MAX_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
-    assert_eq!(MAX_MUL_TWO, None);
+    const MAX_CHECKED_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
+    assert_eq!(MAX_CHECKED_MUL_TWO, None);
 
-    const ONE_DIV_ONE : Option<Duration> = ONE.checked_div(1);
-    assert_eq!(ONE_DIV_ONE, Some(ONE));
+    const ONE_CHECKED_DIV_ONE : Option<Duration> = ONE.checked_div(1);
+    assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE));
 
-    const ONE_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
-    assert_eq!(ONE_DIV_ZERO, None);
+    const ONE_CHECKED_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
+    assert_eq!(ONE_CHECKED_DIV_ZERO, None);
 
     const MAX_AS_F32 : f32 = MAX.as_secs_f32();
     assert_eq!(MAX_AS_F32, 18446744000000000000.0_f32);
@@ -50,6 +51,15 @@ fn duration() {
 
     const ONE_AS_F64 : f64 = ONE.div_duration_f64(ONE);
     assert_eq!(ONE_AS_F64, 1.0_f64);
+
+    const MAX_SATURATING_ADD_ONE : Duration = MAX.saturating_add(ONE);
+    assert_eq!(MAX_SATURATING_ADD_ONE, MAX);
+
+    const ZERO_SATURATING_SUB_ONE : Duration = ZERO.saturating_sub(ONE);
+    assert_eq!(ZERO_SATURATING_SUB_ONE, ZERO);
+
+    const MAX_SATURATING_MUL_TWO : Duration = MAX.saturating_mul(2);
+    assert_eq!(MAX_SATURATING_MUL_TWO, MAX);
 }
 
 fn main() {