diff --git a/CHANGELOG.md b/CHANGELOG.md index 3815505c5..0180f0bc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,12 @@ and this project adheres to `From for i128` ([#2268]) - cosmwasm-std: Deprecate `abort` feature. The panic handler is now always enabled. ([#2337]) +- cosmwasm-std: Implement `Uint128::from_{be,le}_bytes` and + `Uint64::from_{be,le}_bytes`. ([#2269]) [#2268]: https://github.com/CosmWasm/cosmwasm/issues/2268 [#2337]: https://github.com/CosmWasm/cosmwasm/issues/2337 +[#2269]: https://github.com/CosmWasm/cosmwasm/issues/2269 ## Fixed diff --git a/packages/std/src/math/conversion.rs b/packages/std/src/math/conversion.rs index d3ddd6f97..c98bd5dfa 100644 --- a/packages/std/src/math/conversion.rs +++ b/packages/std/src/math/conversion.rs @@ -326,6 +326,35 @@ macro_rules! try_from_int_to_uint { } pub(crate) use try_from_int_to_uint; +macro_rules! from_and_to_bytes { + ($inner: ty, $byte_size: literal) => { + /// Constructs new value from big endian bytes + #[must_use] + pub const fn from_be_bytes(data: [u8; $byte_size]) -> Self { + Self(<$inner>::from_be_bytes(data)) + } + + /// Constructs new value from little endian bytes + #[must_use] + pub const fn from_le_bytes(data: [u8; $byte_size]) -> Self { + Self(<$inner>::from_le_bytes(data)) + } + + /// Returns a copy of the number as big endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_be_bytes(self) -> [u8; $byte_size] { + self.0.to_be_bytes() + } + + /// Returns a copy of the number as little endian bytes. + #[must_use = "this returns the result of the operation, without modifying the original"] + pub const fn to_le_bytes(self) -> [u8; $byte_size] { + self.0.to_le_bytes() + } + }; +} +pub(crate) use from_and_to_bytes; + #[cfg(test)] mod tests { use super::*; diff --git a/packages/std/src/math/int128.rs b/packages/std/src/math/int128.rs index 9d8a060ab..659b3367d 100644 --- a/packages/std/src/math/int128.rs +++ b/packages/std/src/math/int128.rs @@ -14,7 +14,8 @@ use crate::{ }; use super::conversion::{ - forward_try_from, primitive_to_wrapped_int, try_from_int_to_int, wrapped_int_to_primitive, + forward_try_from, from_and_to_bytes, primitive_to_wrapped_int, try_from_int_to_int, + wrapped_int_to_primitive, }; use super::impl_int_serde; use super::num_consts::NumConsts; @@ -67,27 +68,7 @@ impl Int128 { self.0 } - #[must_use] - pub const fn from_be_bytes(data: [u8; 16]) -> Self { - Self(i128::from_be_bytes(data)) - } - - #[must_use] - pub const fn from_le_bytes(data: [u8; 16]) -> Self { - Self(i128::from_le_bytes(data)) - } - - /// Returns a copy of the number as big endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_be_bytes(self) -> [u8; 16] { - self.0.to_be_bytes() - } - - /// Returns a copy of the number as little endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_le_bytes(self) -> [u8; 16] { - self.0.to_le_bytes() - } + from_and_to_bytes!(i128, 16); #[must_use] pub const fn is_zero(&self) -> bool { @@ -521,16 +502,64 @@ mod tests { #[test] fn int128_from_be_bytes_works() { - let num = Int128::from_be_bytes([1; 16]); + // zero + let original = [0; 16]; + let num = Int128::from_be_bytes(original); + assert!(num.is_zero()); + + // one + let original = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + let num = Int128::from_be_bytes(original); + assert_eq!(num.i128(), 1); + + // 258 + let original = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2]; + let num = Int128::from_be_bytes(original); + assert_eq!(num.i128(), 258); + + // 2x roundtrip + let original = [1; 16]; + let num = Int128::from_be_bytes(original); let a: [u8; 16] = num.to_be_bytes(); - assert_eq!(a, [1; 16]); + assert_eq!(a, original); - let be_bytes = [ + let original = [ 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, ]; - let num = Int128::from_be_bytes(be_bytes); + let num = Int128::from_be_bytes(original); let resulting_bytes: [u8; 16] = num.to_be_bytes(); - assert_eq!(be_bytes, resulting_bytes); + assert_eq!(resulting_bytes, original); + } + + #[test] + fn int128_from_le_bytes_works() { + // zero + let original = [0; 16]; + let num = Int128::from_le_bytes(original); + assert!(num.is_zero()); + + // one + let original = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let num = Int128::from_le_bytes(original); + assert_eq!(num.i128(), 1); + + // 258 + let original = [2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let num = Int128::from_le_bytes(original); + assert_eq!(num.i128(), 258); + + // 2x roundtrip + let original = [1; 16]; + let num = Int128::from_le_bytes(original); + let a: [u8; 16] = num.to_le_bytes(); + assert_eq!(a, original); + + let original = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Int128::from_le_bytes(original); + let resulting_bytes: [u8; 16] = num.to_le_bytes(); + assert_eq!(resulting_bytes, original); } #[test] diff --git a/packages/std/src/math/int64.rs b/packages/std/src/math/int64.rs index 42777cb1a..dae40f92f 100644 --- a/packages/std/src/math/int64.rs +++ b/packages/std/src/math/int64.rs @@ -14,7 +14,8 @@ use crate::{ }; use super::conversion::{ - forward_try_from, primitive_to_wrapped_int, try_from_int_to_int, wrapped_int_to_primitive, + forward_try_from, from_and_to_bytes, primitive_to_wrapped_int, try_from_int_to_int, + wrapped_int_to_primitive, }; use super::impl_int_serde; use super::num_consts::NumConsts; @@ -67,27 +68,7 @@ impl Int64 { self.0 } - #[must_use] - pub const fn from_be_bytes(data: [u8; 8]) -> Self { - Self(i64::from_be_bytes(data)) - } - - #[must_use] - pub const fn from_le_bytes(data: [u8; 8]) -> Self { - Self(i64::from_le_bytes(data)) - } - - /// Returns a copy of the number as big endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_be_bytes(self) -> [u8; 8] { - self.0.to_be_bytes() - } - - /// Returns a copy of the number as little endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_le_bytes(self) -> [u8; 8] { - self.0.to_le_bytes() - } + from_and_to_bytes!(i64, 8); #[must_use] pub const fn is_zero(&self) -> bool { @@ -511,14 +492,60 @@ mod tests { #[test] fn int64_from_be_bytes_works() { - let num = Int64::from_be_bytes([1; 8]); + // zero + let original = [0; 8]; + let num = Int64::from_be_bytes(original); + assert!(num.is_zero()); + + // one + let original = [0, 0, 0, 0, 0, 0, 0, 1]; + let num = Int64::from_be_bytes(original); + assert_eq!(num.i64(), 1); + + // 258 + let original = [0, 0, 0, 0, 0, 0, 1, 2]; + let num = Int64::from_be_bytes(original); + assert_eq!(num.i64(), 258); + + // 2x roundtrip + let original = [1; 8]; + let num = Int64::from_be_bytes(original); let a: [u8; 8] = num.to_be_bytes(); - assert_eq!(a, [1; 8]); + assert_eq!(a, original); - let be_bytes = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; - let num = Int64::from_be_bytes(be_bytes); - let resulting_bytes: [u8; 8] = num.to_be_bytes(); - assert_eq!(be_bytes, resulting_bytes); + let original = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Int64::from_be_bytes(original); + let a: [u8; 8] = num.to_be_bytes(); + assert_eq!(a, original); + } + + #[test] + fn int64_from_le_bytes_works() { + // zero + let original = [0; 8]; + let num = Int64::from_le_bytes(original); + assert!(num.is_zero()); + + // one + let original = [1, 0, 0, 0, 0, 0, 0, 0]; + let num = Int64::from_le_bytes(original); + assert_eq!(num.i64(), 1); + + // 258 + let original = [2, 1, 0, 0, 0, 0, 0, 0]; + let num = Int64::from_le_bytes(original); + assert_eq!(num.i64(), 258); + + // 2x roundtrip + let original = [1; 8]; + let num = Int64::from_le_bytes(original); + let a: [u8; 8] = num.to_le_bytes(); + assert_eq!(a, original); + + let original = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Int64::from_le_bytes(original); + let a: [u8; 8] = num.to_le_bytes(); + assert_eq!(a, original); } #[test] diff --git a/packages/std/src/math/uint128.rs b/packages/std/src/math/uint128.rs index b82f4e491..1c06c9838 100644 --- a/packages/std/src/math/uint128.rs +++ b/packages/std/src/math/uint128.rs @@ -16,7 +16,9 @@ use crate::{ Uint256, Uint64, }; -use super::conversion::{forward_try_from, primitive_to_wrapped_int, wrapped_int_to_primitive}; +use super::conversion::{ + forward_try_from, from_and_to_bytes, primitive_to_wrapped_int, wrapped_int_to_primitive, +}; use super::impl_int_serde; use super::num_consts::NumConsts; @@ -73,17 +75,7 @@ impl Uint128 { self.0 } - /// Returns a copy of the number as big endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_be_bytes(self) -> [u8; 16] { - self.0.to_be_bytes() - } - - /// Returns a copy of the number as little endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_le_bytes(self) -> [u8; 16] { - self.0.to_le_bytes() - } + from_and_to_bytes!(u128, 16); #[must_use] pub const fn is_zero(&self) -> bool { @@ -605,6 +597,68 @@ mod tests { ); } + #[test] + fn uint128_from_be_bytes_works() { + // zero + let original = [0; 16]; + let num = Uint128::from_be_bytes(original); + assert!(num.is_zero()); + + // one + let original = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]; + let num = Uint128::from_be_bytes(original); + assert_eq!(num.u128(), 1); + + // 258 + let original = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2]; + let num = Uint128::from_be_bytes(original); + assert_eq!(num.u128(), 258); + + // 2x roundtrip + let original = [1; 16]; + let num = Uint128::from_be_bytes(original); + let a: [u8; 16] = num.to_be_bytes(); + assert_eq!(a, original); + + let original = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Uint128::from_be_bytes(original); + let resulting_bytes: [u8; 16] = num.to_be_bytes(); + assert_eq!(resulting_bytes, original); + } + + #[test] + fn uint128_from_le_bytes_works() { + // zero + let original = [0; 16]; + let num = Uint128::from_le_bytes(original); + assert!(num.is_zero()); + + // one + let original = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let num = Uint128::from_le_bytes(original); + assert_eq!(num.u128(), 1); + + // 258 + let original = [2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let num = Uint128::from_le_bytes(original); + assert_eq!(num.u128(), 258); + + // 2x roundtrip + let original = [1; 16]; + let num = Uint128::from_le_bytes(original); + let a: [u8; 16] = num.to_le_bytes(); + assert_eq!(a, original); + + let original = [ + 0u8, 222u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8, + ]; + let num = Uint128::from_le_bytes(original); + let resulting_bytes: [u8; 16] = num.to_le_bytes(); + assert_eq!(resulting_bytes, original); + } + #[test] fn uint128_convert_into() { let original = Uint128(12345); diff --git a/packages/std/src/math/uint64.rs b/packages/std/src/math/uint64.rs index 3e2aec182..35ae7175d 100644 --- a/packages/std/src/math/uint64.rs +++ b/packages/std/src/math/uint64.rs @@ -15,7 +15,9 @@ use crate::{ Uint128, }; -use super::conversion::{forward_try_from, primitive_to_wrapped_int, wrapped_int_to_primitive}; +use super::conversion::{ + forward_try_from, from_and_to_bytes, primitive_to_wrapped_int, wrapped_int_to_primitive, +}; use super::impl_int_serde; use super::num_consts::NumConsts; @@ -69,17 +71,7 @@ impl Uint64 { self.0 } - /// Returns a copy of the number as big endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_be_bytes(self) -> [u8; 8] { - self.0.to_be_bytes() - } - - /// Returns a copy of the number as little endian bytes. - #[must_use = "this returns the result of the operation, without modifying the original"] - pub const fn to_le_bytes(self) -> [u8; 8] { - self.0.to_le_bytes() - } + from_and_to_bytes!(u64, 8); #[must_use] pub const fn is_zero(&self) -> bool { @@ -572,6 +564,64 @@ mod tests { assert_eq!(one.to_be_bytes(), [0, 0, 0, 0, 0, 0, 0, 1]); } + #[test] + fn uint64_from_be_bytes_works() { + // zero + let original = [0; 8]; + let num = Uint64::from_be_bytes(original); + assert!(num.is_zero()); + + // one + let original = [0, 0, 0, 0, 0, 0, 0, 1]; + let num = Uint64::from_be_bytes(original); + assert_eq!(num.u64(), 1); + + // 258 + let original = [0, 0, 0, 0, 0, 0, 1, 2]; + let num = Uint64::from_be_bytes(original); + assert_eq!(num.u64(), 258); + + // 2x roundtrip + let original = [1; 8]; + let num = Uint64::from_be_bytes(original); + let a: [u8; 8] = num.to_be_bytes(); + assert_eq!(a, original); + + let original = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Uint64::from_be_bytes(original); + let resulting_bytes: [u8; 8] = num.to_be_bytes(); + assert_eq!(resulting_bytes, original); + } + + #[test] + fn uint64_from_le_bytes_works() { + // zero + let original = [0; 8]; + let num = Uint64::from_le_bytes(original); + assert!(num.is_zero()); + + // one + let original = [1, 0, 0, 0, 0, 0, 0, 0]; + let num = Uint64::from_le_bytes(original); + assert_eq!(num.u64(), 1); + + // 258 + let original = [2, 1, 0, 0, 0, 0, 0, 0]; + let num = Uint64::from_le_bytes(original); + assert_eq!(num.u64(), 258); + + // 2x roundtrip + let original = [1; 8]; + let num = Uint64::from_le_bytes(original); + let a: [u8; 8] = num.to_le_bytes(); + assert_eq!(a, original); + + let original = [0u8, 222u8, 0u8, 0u8, 0u8, 1u8, 2u8, 3u8]; + let num = Uint64::from_le_bytes(original); + let resulting_bytes: [u8; 8] = num.to_le_bytes(); + assert_eq!(resulting_bytes, original); + } + #[test] fn uint64_convert_into() { let original = Uint64(12345);