From 189b45fe1f680b92fad974cd073c06afcc5e3286 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Wed, 14 Jun 2023 17:06:32 +0200 Subject: [PATCH 1/6] Implement left shift for all ints --- packages/std/src/math/uint128.rs | 51 +++++++++++++++++++++++++++++++- packages/std/src/math/uint256.rs | 24 ++++++++++----- packages/std/src/math/uint512.rs | 40 ++++++++++++++++++++++++- packages/std/src/math/uint64.rs | 35 +++++++++++++++++++++- 4 files changed, 139 insertions(+), 11 deletions(-) diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 319ea92525..68061fd998 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -1,6 +1,7 @@ use std::fmt::{self}; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shr, ShrAssign, Sub, SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, + Sub, SubAssign, }; use std::str::FromStr; @@ -197,6 +198,22 @@ impl Uint128 { .ok_or_else(|| DivideByZeroError::new(self)) } + pub fn checked_shr(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -441,6 +458,26 @@ impl<'a> Shr<&'a u32> for Uint128 { } } +impl Shl for Uint128 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + Self( + self.u128() + .checked_shl(rhs) + .expect("attempt to shift left with overflow"), + ) + } +} + +impl<'a> Shl<&'a u32> for Uint128 { + type Output = Self; + + fn shl(self, rhs: &'a u32) -> Self::Output { + self.shl(*rhs) + } +} + impl AddAssign for Uint128 { fn add_assign(&mut self, rhs: Uint128) { *self = *self + rhs; @@ -497,6 +534,18 @@ impl<'a> ShrAssign<&'a u32> for Uint128 { } } +impl ShlAssign for Uint128 { + fn shl_assign(&mut self, rhs: u32) { + *self = Shl::::shl(*self, rhs); + } +} + +impl<'a> ShlAssign<&'a u32> for Uint128 { + fn shl_assign(&mut self, rhs: &'a u32) { + *self = Shl::::shl(*self, *rhs); + } +} + impl Serialize for Uint128 { /// Serializes as an integer string using base 10 fn serialize(&self, serializer: S) -> Result diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index 201342e387..ef140bbc84 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -3,8 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, ShrAssign, Sub, - SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, + Sub, SubAssign, }; use std::str::FromStr; @@ -575,12 +575,8 @@ impl Shl for Uint256 { type Output = Self; fn shl(self, rhs: u32) -> Self::Output { - self.checked_shl(rhs).unwrap_or_else(|_| { - panic!( - "left shift error: {} is larger or equal than the number of bits in Uint256", - rhs, - ) - }) + self.checked_shl(rhs) + .expect("attempt to shift left with overflow") } } @@ -628,6 +624,18 @@ impl<'a> ShrAssign<&'a u32> for Uint256 { } } +impl ShlAssign for Uint256 { + fn shl_assign(&mut self, rhs: u32) { + *self = self.shl(rhs); + } +} + +impl<'a> ShlAssign<&'a u32> for Uint256 { + fn shl_assign(&mut self, rhs: &'a u32) { + *self = self.shl(*rhs); + } +} + impl Serialize for Uint256 { /// Serializes as an integer string using base 10 fn serialize(&self, serializer: S) -> Result diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index 73a0512f26..36523c9900 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -3,7 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shr, ShrAssign, Sub, SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, + Sub, SubAssign, }; use std::str::FromStr; @@ -262,6 +263,14 @@ impl Uint512 { Ok(Self(self.0.shr(other))) } + pub fn checked_shl(self, other: u32) -> Result { + if other >= 512 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { @@ -542,6 +551,23 @@ impl<'a> Shr<&'a u32> for Uint512 { } } +impl Shl for Uint512 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + self.checked_shl(rhs) + .expect("attempt to shift left with overflow") + } +} + +impl<'a> Shl<&'a u32> for Uint512 { + type Output = Self; + + fn shl(self, rhs: &'a u32) -> Self::Output { + self.shl(*rhs) + } +} + impl AddAssign for Uint512 { fn add_assign(&mut self, rhs: Uint512) { self.0 = self.0.checked_add(rhs.0).unwrap(); @@ -578,6 +604,18 @@ impl<'a> ShrAssign<&'a u32> for Uint512 { } } +impl ShlAssign for Uint512 { + fn shl_assign(&mut self, rhs: u32) { + *self = self.shl(rhs); + } +} + +impl<'a> ShlAssign<&'a u32> for Uint512 { + fn shl_assign(&mut self, rhs: &'a u32) { + *self = self.shl(*rhs); + } +} + impl Serialize for Uint512 { /// Serializes as an integer string using base 10 fn serialize(&self, serializer: S) -> Result diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index e7a50339c0..37b7dcb9c4 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -3,7 +3,8 @@ use schemars::JsonSchema; use serde::{de, ser, Deserialize, Deserializer, Serialize}; use std::fmt::{self}; use std::ops::{ - Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shr, ShrAssign, Sub, SubAssign, + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, + Sub, SubAssign, }; use crate::errors::{ @@ -412,6 +413,26 @@ impl<'a> Shr<&'a u32> for Uint64 { } } +impl Shl for Uint64 { + type Output = Self; + + fn shl(self, rhs: u32) -> Self::Output { + Self( + self.u64() + .checked_shl(rhs) + .expect("attempt to shift left with overflow"), + ) + } +} + +impl<'a> Shl<&'a u32> for Uint64 { + type Output = Self; + + fn shl(self, rhs: &'a u32) -> Self::Output { + self.shl(*rhs) + } +} + impl AddAssign for Uint64 { fn add_assign(&mut self, rhs: Uint64) { self.0 = self.0.checked_add(rhs.u64()).unwrap(); @@ -448,6 +469,18 @@ impl<'a> ShrAssign<&'a u32> for Uint64 { } } +impl ShlAssign for Uint64 { + fn shl_assign(&mut self, rhs: u32) { + *self = self.shl(rhs); + } +} + +impl<'a> ShlAssign<&'a u32> for Uint64 { + fn shl_assign(&mut self, rhs: &'a u32) { + *self = self.shl(*rhs); + } +} + impl Serialize for Uint64 { /// Serializes as an integer string using base 10 fn serialize(&self, serializer: S) -> Result From b730f4b237a9ebdd076ca0537a2e3c24683f3984 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 15 Jun 2023 12:46:36 +0200 Subject: [PATCH 2/6] Add check trait for shift operations --- packages/std/src/math/mod.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/std/src/math/mod.rs b/packages/std/src/math/mod.rs index 706d23005d..72e55d9f1d 100644 --- a/packages/std/src/math/mod.rs +++ b/packages/std/src/math/mod.rs @@ -21,7 +21,7 @@ mod tests { use super::*; use std::ops::*; - /// An trait that ensures other traits are implemented for our number types + /// A trait that ensures other traits are implemented for our number types trait AllImpl<'a>: Add + Add<&'a Self> @@ -50,10 +50,28 @@ mod tests { { } + /// A trait that ensures other traits are implemented for our integer types + trait IntImpl<'a>: + AllImpl<'a> + + Shl + + Shl<&'a u32> + + ShlAssign + + ShlAssign<&'a u32> + + Shr + + Shr<&'a u32> + + ShrAssign + + ShrAssign<&'a u32> + { + } + impl AllImpl<'_> for Uint64 {} impl AllImpl<'_> for Uint128 {} impl AllImpl<'_> for Uint256 {} impl AllImpl<'_> for Uint512 {} + impl IntImpl<'_> for Uint64 {} + impl IntImpl<'_> for Uint128 {} + impl IntImpl<'_> for Uint256 {} + impl IntImpl<'_> for Uint512 {} impl AllImpl<'_> for Decimal {} impl AllImpl<'_> for Decimal256 {} } From 5635657f8b7bb16fa6da283b9025dd7cc813e448 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 15 Jun 2023 13:26:41 +0200 Subject: [PATCH 3/6] Add test for left and right shifts --- packages/std/src/math/uint128.rs | 38 ++++++++++++++++++++++++++++++++ packages/std/src/math/uint256.rs | 21 ++++++++++++++++++ packages/std/src/math/uint512.rs | 25 +++++++++++++++++++++ packages/std/src/math/uint64.rs | 34 ++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 68061fd998..207f689173 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -938,6 +938,44 @@ mod tests { ); } + #[test] + fn uint128_shr_works() { + let original = Uint128::new(u128::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8, + ])); + + let shifted = Uint128::new(u128::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ])); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn uint128_shr_overflow_panics() { + let _ = Uint128::from(1u32) >> 128u32; + } + + #[test] + fn uint128_shl_works() { + let original = Uint128::new(u128::from_be_bytes([ + 64u8, 128u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ])); + + let shifted = Uint128::new(u128::from_be_bytes([ + 2u8, 0u8, 4u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ])); + + assert_eq!(original << 2u32, shifted); + } + + #[test] + #[should_panic] + fn uint128_shl_overflow_panics() { + let _ = Uint128::from(1u32) << 128u32; + } + #[test] fn sum_works() { let nums = vec![Uint128(17), Uint128(123), Uint128(540), Uint128(82)]; diff --git a/packages/std/src/math/uint256.rs b/packages/std/src/math/uint256.rs index ef140bbc84..e074be4a45 100644 --- a/packages/std/src/math/uint256.rs +++ b/packages/std/src/math/uint256.rs @@ -1504,6 +1504,27 @@ mod tests { let _ = Uint256::from(1u32) >> 256u32; } + #[test] + fn uint256_shl_works() { + let original = Uint256::new([ + 64u8, 128u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]); + + let shifted = Uint256::new([ + 2u8, 0u8, 4u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]); + + assert_eq!(original << 2u32, shifted); + } + + #[test] + #[should_panic] + fn uint256_shl_overflow_panics() { + let _ = Uint256::from(1u32) << 256u32; + } + #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/uint512.rs b/packages/std/src/math/uint512.rs index 36523c9900..16298531ac 100644 --- a/packages/std/src/math/uint512.rs +++ b/packages/std/src/math/uint512.rs @@ -1157,6 +1157,31 @@ mod tests { let _ = Uint512::from(1u32) >> 512u32; } + #[test] + fn uint512_shl_works() { + let original = Uint512::new([ + 64u8, 128u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]); + + let shifted = Uint512::new([ + 2u8, 0u8, 4u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ]); + + assert_eq!(original << 2u32, shifted); + } + + #[test] + #[should_panic] + fn uint512_shl_overflow_panics() { + let _ = Uint512::from(1u32) << 512u32; + } + #[test] fn sum_works() { let nums = vec![ diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index 37b7dcb9c4..a8bfba170d 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -833,6 +833,40 @@ mod tests { ); } + #[test] + fn uint64_shr_works() { + let original = Uint64::new(u64::from_be_bytes([0u8, 0u8, 0u8, 0u8, 2u8, 0u8, 4u8, 2u8])); + + let shifted = Uint64::new(u64::from_be_bytes([ + 0u8, 0u8, 0u8, 0u8, 0u8, 128u8, 1u8, 0u8, + ])); + + assert_eq!(original >> 2u32, shifted); + } + + #[test] + #[should_panic] + fn uint64_shr_overflow_panics() { + let _ = Uint64::from(1u32) >> 64u32; + } + + #[test] + fn uint64_shl_works() { + let original = Uint64::new(u64::from_be_bytes([ + 64u8, 128u8, 1u8, 0u8, 0u8, 0u8, 0u8, 0u8, + ])); + + let shifted = Uint64::new(u64::from_be_bytes([2u8, 0u8, 4u8, 0u8, 0u8, 0u8, 0u8, 0u8])); + + assert_eq!(original << 2u32, shifted); + } + + #[test] + #[should_panic] + fn uint64_shl_overflow_panics() { + let _ = Uint64::from(1u32) << 64u32; + } + #[test] fn sum_works() { let nums = vec![Uint64(17), Uint64(123), Uint64(540), Uint64(82)]; From a1c24f97c48aaff8fc1dd47b4c2e2f164ed8795c Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 15 Jun 2023 15:37:44 +0200 Subject: [PATCH 4/6] Fix checked_shr and checked_shl for Uint128 Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- packages/std/src/math/uint128.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index 207f689173..9c58c2cc3b 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -199,7 +199,7 @@ impl Uint128 { } pub fn checked_shr(self, other: u32) -> Result { - if other >= 512 { + if other >= 128 { return Err(OverflowError::new(OverflowOperation::Shr, self, other)); } @@ -207,7 +207,7 @@ impl Uint128 { } pub fn checked_shl(self, other: u32) -> Result { - if other >= 512 { + if other >= 128 { return Err(OverflowError::new(OverflowOperation::Shl, self, other)); } From e42306dd68f43d2f9120cbdf1ee39d53a144cdd0 Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 15 Jun 2023 15:50:08 +0200 Subject: [PATCH 5/6] Add checked_shl and checked_shr to Uint64 --- packages/std/src/math/uint64.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index a8bfba170d..9afd119647 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -191,6 +191,22 @@ impl Uint64 { .ok_or_else(|| DivideByZeroError::new(self)) } + pub fn checked_shr(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shr, self, other)); + } + + Ok(Self(self.0.shr(other))) + } + + pub fn checked_shl(self, other: u32) -> Result { + if other >= 64 { + return Err(OverflowError::new(OverflowOperation::Shl, self, other)); + } + + Ok(Self(self.0.shl(other))) + } + #[must_use = "this returns the result of the operation, without modifying the original"] #[inline] pub fn wrapping_add(self, other: Self) -> Self { From d237c58e4bdad448407276c13d76758b2b1ad1db Mon Sep 17 00:00:00 2001 From: Christoph Otter Date: Thu, 15 Jun 2023 16:56:55 +0200 Subject: [PATCH 6/6] Add changelog entry --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7d36929d7..d461d0fbe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to ## [Unreleased] +## Added + +- cosmwasm-std: Add `<<` and `<<=` implementation for `Uint{64,128,256,512}` + types. ([#1723]) + +[#1723]: https://github.com/CosmWasm/cosmwasm/pull/1723 + ## [1.2.6] - 2023-06-05 ## Changed