diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ab63036c2..94f19a0ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,13 @@ and this project adheres to - cosmwasm-std: Implement `&T + T` and `&T op &T` for `Uint64`, `Uint128`, `Uint256` and `Uint512`; improve panic message for `Uint64::add` and `Uint512::add` ([#2092]) +- cosmwasm-std: Add `Uint{64,128,256,512}::panicking_add` and `::panicking_sub` + which are like the `Add`/`Sub` implementations but `const`. ([#2098]) +- cosmwasm-std: Let `Timestamp::plus_nanos`/`::minus_nanos` use + `Uint64::panicking_add`/`::panicking_sub` and document overflows. ([#2098]) [#2092]: https://github.com/CosmWasm/cosmwasm/pull/2092 +[#2098]: https://github.com/CosmWasm/cosmwasm/pull/2098 ### Changed diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index db59d469d3..70bdf11c54 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -259,6 +259,28 @@ impl Uint128 { Self(self.0.saturating_pow(exp)) } + /// This is the same as [`Uint128::add`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_add(self, other: Self) -> Self { + match self.0.checked_add(other.u128()) { + None => panic!("attempt to add with overflow"), + Some(sum) => Self(sum), + } + } + + /// This is the same as [`Uint128::sub`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_sub(self, other: Self) -> Self { + match self.0.checked_sub(other.u128()) { + None => panic!("attempt to subtract with overflow"), + Some(diff) => Self(diff), + } + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn abs_diff(self, other: Self) -> Self { Self(if self.0 < other.0 { @@ -363,11 +385,7 @@ impl Add for Uint128 { type Output = Self; fn add(self, rhs: Self) -> Self { - Self( - self.u128() - .checked_add(rhs.u128()) - .expect("attempt to add with overflow"), - ) + self.panicking_add(rhs) } } forward_ref_binop!(impl Add, add for Uint128, Uint128); @@ -376,11 +394,7 @@ impl Sub for Uint128 { type Output = Self; fn sub(self, rhs: Self) -> Self { - Uint128( - self.u128() - .checked_sub(rhs.u128()) - .expect("attempt to subtract with overflow"), - ) + self.panicking_sub(rhs) } } forward_ref_binop!(impl Sub, sub for Uint128, Uint128); @@ -1148,6 +1162,21 @@ mod tests { assert_eq!(a, Uint128::from(1u32)); } + #[test] + fn uint128_panicking_sub_works() { + let a = Uint128::new(5); + let b = Uint128::new(3); + assert_eq!(a.panicking_sub(b), Uint128::new(2)); + } + + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn uint128_panicking_sub_panics_on_overflow() { + let a = Uint128::zero(); + let b = Uint128::one(); + let _diff = a.panicking_sub(b); + } + #[test] fn uint128_abs_diff_works() { let a = Uint128::from(42u32); diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index 3e1d55971e..22749837b6 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -329,6 +329,28 @@ impl Uint256 { Self(self.0.saturating_pow(exp)) } + /// This is the same as [`Uint256::add`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_add(self, other: Self) -> Self { + match self.0.checked_add(other.0) { + None => panic!("attempt to add with overflow"), + Some(sum) => Self(sum), + } + } + + /// This is the same as [`Uint256::sub`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_sub(self, other: Self) -> Self { + match self.0.checked_sub(other.0) { + None => panic!("attempt to subtract with overflow"), + Some(diff) => Self(diff), + } + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn abs_diff(self, other: Self) -> Self { Self(self.0.abs_diff(other.0)) @@ -428,11 +450,7 @@ impl Add for Uint256 { type Output = Self; fn add(self, rhs: Self) -> Self { - Self( - self.0 - .checked_add(rhs.0) - .expect("attempt to add with overflow"), - ) + self.panicking_add(rhs) } } forward_ref_binop!(impl Add, add for Uint256, Uint256); @@ -441,11 +459,7 @@ impl Sub for Uint256 { type Output = Self; fn sub(self, rhs: Self) -> Self { - Self( - self.0 - .checked_sub(rhs.0) - .expect("attempt to subtract with overflow"), - ) + self.panicking_sub(rhs) } } forward_ref_binop!(impl Sub, sub for Uint256, Uint256); @@ -1690,6 +1704,21 @@ mod tests { assert_eq!(a, Uint256::from(1u32)); } + #[test] + fn uint256_panicking_sub_works() { + let a = Uint256::from(5u32); + let b = Uint256::from(3u32); + assert_eq!(a.panicking_sub(b), Uint256::from(2u32)); + } + + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn uint256_panicking_sub_panics_on_overflow() { + let a = Uint256::zero(); + let b = Uint256::one(); + let _diff = a.panicking_sub(b); + } + #[test] fn uint256_abs_diff_works() { let a = Uint256::from(42u32); diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index a6de3592b8..53b1fc7892 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -294,6 +294,28 @@ impl Uint512 { Self(self.0.saturating_pow(exp)) } + /// This is the same as [`Uint512::add`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_add(self, other: Self) -> Self { + match self.0.checked_add(other.0) { + None => panic!("attempt to add with overflow"), + Some(sum) => Self(sum), + } + } + + /// This is the same as [`Uint512::sub`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_sub(self, other: Self) -> Self { + match self.0.checked_sub(other.0) { + None => panic!("attempt to subtract with overflow"), + Some(diff) => Self(diff), + } + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn abs_diff(self, other: Self) -> Self { Self(self.0.abs_diff(other.0)) @@ -415,11 +437,7 @@ impl Add for Uint512 { type Output = Self; fn add(self, rhs: Self) -> Self { - Self( - self.0 - .checked_add(rhs.0) - .expect("attempt to add with overflow"), - ) + self.panicking_add(rhs) } } forward_ref_binop!(impl Add, add for Uint512, Uint512); @@ -428,7 +446,7 @@ impl Sub for Uint512 { type Output = Self; fn sub(self, rhs: Self) -> Self { - Uint512(self.0.checked_sub(rhs.0).unwrap()) + self.panicking_sub(rhs) } } forward_ref_binop!(impl Sub, sub for Uint512, Uint512); @@ -1338,6 +1356,21 @@ mod tests { assert_eq!(a, Uint512::from(1u32)); } + #[test] + fn uint512_panicking_sub_works() { + let a = Uint512::from(5u32); + let b = Uint512::from(3u32); + assert_eq!(a.panicking_sub(b), Uint512::from(2u32)); + } + + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn uint512_panicking_sub_panics_on_overflow() { + let a = Uint512::zero(); + let b = Uint512::one(); + let _diff = a.panicking_sub(b); + } + #[test] fn uint512_abs_diff_works() { let a = Uint512::from(42u32); diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index e71abd977a..a865c8a95c 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -252,6 +252,28 @@ impl Uint64 { Self(self.0.saturating_pow(exp)) } + /// This is the same as [`Uint64::add`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_add(self, other: Self) -> Self { + match self.0.checked_add(other.u64()) { + None => panic!("attempt to add with overflow"), + Some(sum) => Self(sum), + } + } + + /// This is the same as [`Uint64::sub`] but const. + /// + /// Panics on overflow. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn panicking_sub(self, other: Self) -> Self { + match self.0.checked_sub(other.u64()) { + None => panic!("attempt to subtract with overflow"), + Some(diff) => Self(diff), + } + } + #[must_use = "this returns the result of the operation, without modifying the original"] pub const fn abs_diff(self, other: Self) -> Self { Self(if self.0 < other.0 { @@ -326,11 +348,7 @@ impl Add for Uint64 { type Output = Self; fn add(self, rhs: Self) -> Self { - Self( - self.u64() - .checked_add(rhs.u64()) - .expect("attempt to add with overflow"), - ) + self.panicking_add(rhs) } } forward_ref_binop!(impl Add, add for Uint64, Uint64); @@ -339,11 +357,7 @@ impl Sub for Uint64 { type Output = Self; fn sub(self, rhs: Self) -> Self { - Uint64( - self.u64() - .checked_sub(rhs.u64()) - .expect("attempt to subtract with overflow"), - ) + self.panicking_sub(rhs) } } forward_ref_binop!(impl Sub, sub for Uint64, Uint64); @@ -1062,6 +1076,21 @@ mod tests { assert_eq!(a, Uint64::from(1u32)); } + #[test] + fn uint64_panicking_sub_works() { + let a = Uint64::new(5); + let b = Uint64::new(3); + assert_eq!(a.panicking_sub(b), Uint64::new(2)); + } + + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn uint64_panicking_sub_panics_on_overflow() { + let a = Uint64::zero(); + let b = Uint64::one(); + let _diff = a.panicking_sub(b); + } + #[test] fn uint64_abs_diff_works() { let a = Uint64::from(42u32); diff --git a/packages/std/src/timestamp.rs b/packages/std/src/timestamp.rs index 6a10f060d5..70167b8285 100644 --- a/packages/std/src/timestamp.rs +++ b/packages/std/src/timestamp.rs @@ -38,62 +38,105 @@ impl Timestamp { Timestamp(Uint64::new(seconds_since_epoch * 1_000_000_000)) } + /// Adds the given amount of days to the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result exceeds the value range of [`Timestamp`]. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn plus_days(&self, addition: u64) -> Timestamp { self.plus_hours(addition * 24) } + /// Adds the given amount of hours to the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result exceeds the value range of [`Timestamp`]. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn plus_hours(&self, addition: u64) -> Timestamp { self.plus_minutes(addition * 60) } + /// Adds the given amount of minutes to the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result exceeds the value range of [`Timestamp`]. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn plus_minutes(&self, addition: u64) -> Timestamp { self.plus_seconds(addition * 60) } + /// Adds the given amount of seconds to the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result exceeds the value range of [`Timestamp`]. #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] pub const fn plus_seconds(&self, addition: u64) -> Timestamp { self.plus_nanos(addition * 1_000_000_000) } + /// Adds the given amount of nanoseconds to the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result exceeds the value range of [`Timestamp`]. #[must_use = "this returns the result of the operation, without modifying the original"] + // no #[inline] here as this could be shared with all the callers pub const fn plus_nanos(&self, addition: u64) -> Timestamp { - let nanos = Uint64::new(self.0.u64() + addition); + let nanos = self.0.panicking_add(Uint64::new(addition)); Timestamp(nanos) } + /// Subtracts the given amount of days from the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn minus_days(&self, subtrahend: u64) -> Timestamp { self.minus_hours(subtrahend * 24) } + /// Subtracts the given amount of hours from the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn minus_hours(&self, subtrahend: u64) -> Timestamp { self.minus_minutes(subtrahend * 60) } + /// Subtracts the given amount of minutes from the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented. #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub const fn minus_minutes(&self, subtrahend: u64) -> Timestamp { self.minus_seconds(subtrahend * 60) } + /// Subtracts the given amount of seconds from the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented. #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline] pub const fn minus_seconds(&self, subtrahend: u64) -> Timestamp { self.minus_nanos(subtrahend * 1_000_000_000) } + /// Subtracts the given amount of nanoseconds from the timestamp and + /// returns the result. The original value remains unchanged. + /// + /// Panics if the result is not >= 0. I.e. times before epoch cannot be represented. #[must_use = "this returns the result of the operation, without modifying the original"] + // no #[inline] here as this could be shared with all the callers pub const fn minus_nanos(&self, subtrahend: u64) -> Timestamp { - let nanos = Uint64::new(self.0.u64() - subtrahend); - Timestamp(nanos) + Timestamp(self.0.panicking_sub(Uint64::new(subtrahend))) } /// Returns nanoseconds since epoch @@ -160,6 +203,13 @@ mod tests { assert_eq!(sum.0.u64(), 123); } + #[test] + #[should_panic(expected = "attempt to add with overflow")] + fn timestamp_plus_nanos_panics_on_overflow() { + let max = Timestamp::from_nanos(u64::MAX); + let _earlier = max.plus_nanos(20); + } + #[test] fn timestamp_minus_seconds() { let earlier = Timestamp::from_seconds(123).minus_seconds(0);