diff --git a/sway-lib-core/src/prelude.sw b/sway-lib-core/src/prelude.sw index d19e3808f6f..00fb4e4c420 100644 --- a/sway-lib-core/src/prelude.sw +++ b/sway-lib-core/src/prelude.sw @@ -4,6 +4,7 @@ library; //! The prelude consists of implicitly available items, //! for which `use` is not required. use ::primitives::*; +use ::primitive_conversions::*; use ::raw_ptr::*; use ::raw_slice::*; use ::never::*; diff --git a/sway-lib-core/src/primitive_conversions.sw b/sway-lib-core/src/primitive_conversions.sw index 72fd6506297..eca59b66645 100644 --- a/sway-lib-core/src/primitive_conversions.sw +++ b/sway-lib-core/src/primitive_conversions.sw @@ -1,6 +1,28 @@ library; impl u64 { + /// Extends a `u64` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u64` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self); + asm(input: input) { + input: u256 + } + } + /// Converts the `u64` to a sequence of little-endian bytes. /// /// # Returns @@ -397,6 +419,31 @@ impl u32 { } } +// TODO: This must be in a seperate impl block until https://github.com/FuelLabs/sway/issues/1548 is resolved +impl u32 { + /// Extends a `u32` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u32` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u32; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self.as_u64()); + asm(input: input) { + input: u256 + } + } +} + impl u16 { /// Extends a `u16` to a `u32`. /// @@ -560,6 +607,31 @@ impl u16 { } } +// TODO: This must be in a seperate impl block until https://github.com/FuelLabs/sway/issues/1548 is resolved +impl u16 { + /// Extends a `u16` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u16` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u16; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self.as_u64()); + asm(input: input) { + input: u256 + } + } +} + impl u8 { /// Extends a `u8` to a `u16`. /// @@ -625,7 +697,53 @@ impl u8 { } } +// TODO: This must be in a seperate impl block until https://github.com/FuelLabs/sway/issues/1548 is resolved +impl u8 { + /// Extends a `u8` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `u8` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val = 2u8; + /// let result = val.as_u256(); + /// assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + let input = (0u64, 0u64, 0u64, self.as_u64()); + asm(input: input) { + input: u256 + } + } +} + impl b256 { + /// Converts a `b256` to a `u256`. + /// + /// # Returns + /// + /// * [u256] - The converted `b256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val: b256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20; + /// let result = val.as_u256(); + /// assert(result == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); + /// } + /// ``` + pub fn as_u256(self) -> u256 { + asm(input: self) { + input: u256 + } + } + /// Converts the `b256` to a sequence of little-endian bytes. /// /// # Returns @@ -771,12 +889,186 @@ impl b256 { } } +impl u256 { + /// Converts a `u256` to a `b256`. + /// + /// # Returns + /// + /// * [b256] - The converted `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let val: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + /// let result = val.as_b256(); + /// assert(result == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20); + /// } + /// ``` + pub fn as_b256(self) -> b256 { + asm(input: self) { + input: b256 + } + } + + /// Converts the `u256` to a sequence of little-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 32]] - An array of 32 `u8` bytes that compose the `u256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + /// 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + /// 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + /// 2_u8, 1_u8]; + /// + /// let x = u256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); + /// } + /// ``` + pub fn to_le_bytes(self) -> [u8; 32] { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_le_bytes(); + let b = b.to_le_bytes(); + let c = c.to_le_bytes(); + let d = d.to_le_bytes(); + + let (a,b,c,d) = (d,c,b,a); + + let output = [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]]; + + output + } + + /// Converts a sequence of little-endian bytes to a `u256`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 32]] - A sequence of 32 `u8` bytes that represent a `u256`. + /// + /// # Returns + /// + /// * [u256] - The resulting `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + /// 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + /// 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + /// 2_u8, 1_u8]; + /// + /// let x = u256::from_le_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + /// ``` + pub fn from_le_bytes(bytes: [u8; 32]) -> Self { + let a = u64::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]); + let b = u64::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]]); + let c = u64::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]); + let d = u64::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31]]); + + let result = (d, c, b, a); + + asm(r1: result) { + r1: u256 + } + } + + /// Converts the `u256` to a sequence of big-endian bytes. + /// + /// # Returns + /// + /// * [[u8; 32]] - An array of 32 `u8` bytes that compose the `u256`. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + /// let bytes = x.to_be_bytes(); + /// + /// let mut i: u8 = 0; + /// while i < 32_u8 { + /// assert(bytes[i.as_u64()] == i + 1_u8); + /// i += 1_u8; + /// } + /// } + /// ``` + pub fn to_be_bytes(self) -> [u8; 32] { + let (a, b, c, d): (u64, u64, u64, u64) = asm(r1: self) {r1: (u64, u64, u64, u64)}; + let a = a.to_be_bytes(); + let b = b.to_be_bytes(); + let c = c.to_be_bytes(); + let d = d.to_be_bytes(); + + let output = [a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], + c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]]; + + output + } + + /// Converts a sequence of big-endian bytes to a `u256`. + /// + /// # Arguments + /// + /// * `bytes`: [[u8; 32]] - A sequence of 32 `u8` bytes that represent a `u256`. + /// + /// # Returns + /// + /// * [u256] - The resulting `u256` value. + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8, 9_u8, 10_u8, + /// 11_u8, 12_u8, 13_u8, 14_u8, 15_u8, 16_u8, 17_u8, 18_u8, 19_u8, 20_u8, + /// 21_u8, 22_u8, 23_u8, 24_u8, 25_u8, 26_u8, 27_u8, 28_u8, 29_u8, 30_u8, + /// 31_u8, 32_u8]; + /// let x = u256::from_be_bytes(bytes); + /// + /// assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); + /// } + /// ``` + pub fn from_be_bytes(bytes: [u8; 32]) -> Self { + let a = u64::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]); + let b = u64::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]]); + let c = u64::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]]); + let d = u64::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31]]); + + let result = (a, b, c, d); + + asm(r1: result) { + r1: u256 + } + } +} + fn assert(condition: bool) { if !condition { __revert(0) } } +#[test] +fn test_u64_as_u256() { + let val = 2; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + #[test] fn test_u64_to_le_bytes() { let x: u64 = 578437695752307201; @@ -823,6 +1115,20 @@ fn test_u64_from_be_bytes() { assert(result == 578437695752307201); } +#[test] +fn test_u32_as_u64() { + let val = 2u32; + let result = val.as_u64(); + assert(result == 2); +} + +#[test] +fn test_u32_as_u256() { + let val = 2u32; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + #[test] fn test_u32_to_le_bytes() { let x: u32 = 67305985; @@ -861,6 +1167,27 @@ fn test_u32_from_be_bytes() { assert(result == 67305985_u32); } +#[test] +fn test_u16_as_u64() { + let val = 2u16; + let result = val.as_u64(); + assert(result == 2); +} + +#[test] +fn test_u16_as_u32() { + let val = 2u16; + let result = val.as_u32(); + assert(result == 2u32); +} + +#[test] +fn test_u16_as_u256() { + let val = 2u16; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + #[test] fn test_u16_to_le_bytes() { let x: u16 = 513; @@ -895,6 +1222,41 @@ fn test_u16_from_be_bytes() { assert(result == 513_u16); } +#[test] +fn test_u8_as_u64() { + let val = 2u8; + let result = val.as_u64(); + assert(result == 2); +} + +#[test] +fn test_u8_as_u32() { + let val = 2u8; + let result = val.as_u32(); + assert(result == 2u32); +} + +#[test] +fn test_u8_as_u16() { + let val = 2u8; + let result = val.as_u16(); + assert(result == 2u16); +} + +#[test] +fn test_u8_as_u256() { + let val = 2u8; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + +#[test] +fn test_b256_as_u256() { + let val = 0x0000000000000000000000000000000000000000000000000000000000000002; + let result = val.as_u256(); + assert(result == 0x0000000000000000000000000000000000000000000000000000000000000002u256); +} + #[test] fn test_b256_from_le_bytes() { let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, @@ -945,3 +1307,55 @@ fn test_b256_to_be_bytes() { i += 1_u8; } } + + +#[test] +fn test_u256_from_le_bytes() { + let bytes = [32_u8, 31_u8, 30_u8, 29_u8, 28_u8, 27_u8, 26_u8, 25_u8, 24_u8, 23_u8, + 22_u8, 21_u8, 20_u8, 19_u8, 18_u8, 17_u8, 16_u8, 15_u8, 14_u8, 13_u8, + 12_u8, 11_u8, 10_u8, 9_u8, 8_u8, 7_u8, 6_u8, 5_u8, 4_u8, 3_u8, + 2_u8, 1_u8]; + + let x = u256::from_le_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); +} + +#[test] +fn test_u256_to_le_bytes() { + let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + + let bytes = x.to_le_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes[i.as_u64()] == 32_u8 - i); + i += 1_u8; + } + +} + +#[test] +fn test_u256_from_be_bytes() { + let bytes = [1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8, 9_u8, 10_u8, + 11_u8, 12_u8, 13_u8, 14_u8, 15_u8, 16_u8, 17_u8, 18_u8, 19_u8, 20_u8, + 21_u8, 22_u8, 23_u8, 24_u8, 25_u8, 26_u8, 27_u8, 28_u8, 29_u8, 30_u8, + 31_u8, 32_u8]; + + let x = u256::from_be_bytes(bytes); + + assert(x == 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256); +} + +#[test] +fn test_u256_to_be_bytes() { + let x: u256 = 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20u256; + + let bytes = x.to_be_bytes(); + + let mut i: u8 = 0; + while i < 32_u8 { + assert(bytes[i.as_u64()] == i + 1_u8); + i += 1_u8; + } +} diff --git a/sway-lib-std/src/lib.sw b/sway-lib-std/src/lib.sw index ba0dfb7feae..b3c31bea190 100644 --- a/sway-lib-std/src/lib.sw +++ b/sway-lib-std/src/lib.sw @@ -6,13 +6,13 @@ pub mod revert; pub mod result; pub mod option; pub mod assert; -pub mod primitive_conversions; pub mod convert; pub mod intrinsics; pub mod alloc; pub mod registers; pub mod vec; pub mod bytes; +pub mod primitive_conversions; pub mod math; pub mod flags; pub mod u128; @@ -33,7 +33,6 @@ pub mod ecr; pub mod vm; pub mod string; pub mod r#storage; -pub mod b256; pub mod block; pub mod inputs; pub mod auth; diff --git a/sway-lib-std/src/primitive_conversions.sw b/sway-lib-std/src/primitive_conversions.sw index 2c0e7389f4c..99d170048fa 100644 --- a/sway-lib-std/src/primitive_conversions.sw +++ b/sway-lib-std/src/primitive_conversions.sw @@ -1,106 +1,8 @@ library; -use ::option::Option::{self, *}; -use ::assert::*; -use core::primitive_conversions::*; - -impl u16 { - pub fn try_as_u8(self) -> Option { - if self <= u8::max().as_u16() { - Some(asm(input: self) { - input: u8 - }) - } else { - None - } - } -} - -impl u32 { - pub fn try_as_u8(self) -> Option { - if self <= u8::max().as_u32() { - Some(asm(input: self) { - input: u8 - }) - } else { - None - } - } - - pub fn try_as_u16(self) -> Option { - if self <= u16::max().as_u32() { - Some(asm(input: self) { - input: u16 - }) - } else { - None - } - } -} - -impl u64 { - pub fn try_as_u8(self) -> Option { - if self <= u8::max().as_u64() { - Some(asm(input: self) { - input: u8 - }) - } else { - None - } - } - - pub fn try_as_u16(self) -> Option { - if self <= u16::max().as_u64() { - Some(asm(input: self) { - input: u16 - }) - } else { - None - } - } - - pub fn try_as_u32(self) -> Option { - if self <= u32::max().as_u64() { - Some(asm(input: self) { - input: u32 - }) - } else { - None - } - } -} - -impl str { - pub fn try_as_str_array(self) -> Option { - __assert_is_str_array::(); - let str_size = __size_of_str_array::(); - let source = self.as_ptr(); - - if self.len() == str_size { - let s: S = asm(str_size: str_size, source: source, dest) { - move dest sp; - cfe str_size; - mcp dest source str_size; - dest: S - }; - asm (str_size: str_size) { - cfs str_size; - } - Some(s) - } else { - None - } - } -} - -#[test] -fn str_slice_to_str_array() { - use ::assert::*; - use core::str::*; - - let a = "abcd"; - let b: str[4] = a.try_as_str_array().unwrap(); - let c = from_str_array(b); - - assert(a == c); -} \ No newline at end of file +pub mod b256; +pub mod r#str; +pub mod u8; +pub mod u16; +pub mod u32; +pub mod u64; diff --git a/sway-lib-std/src/b256.sw b/sway-lib-std/src/primitive_conversions/b256.sw similarity index 96% rename from sway-lib-std/src/b256.sw rename to sway-lib-std/src/primitive_conversions/b256.sw index 80909e4e93c..19da87cc854 100644 --- a/sway-lib-std/src/b256.sw +++ b/sway-lib-std/src/primitive_conversions/b256.sw @@ -1,10 +1,8 @@ library; -use ::assert::assert; use ::bytes::Bytes; use ::convert::TryFrom; use ::option::Option::{self, *}; -use ::logging::log; impl TryFrom for b256 { fn try_from(b: Bytes) -> Option { @@ -21,6 +19,8 @@ impl TryFrom for b256 { #[test] fn test_b256_try_from() { + use ::assert::assert; + let mut initial_bytes = Bytes::with_capacity(32); let mut i = 0; while i < 32 { diff --git a/sway-lib-std/src/primitive_conversions/str.sw b/sway-lib-std/src/primitive_conversions/str.sw new file mode 100644 index 00000000000..7a590424a90 --- /dev/null +++ b/sway-lib-std/src/primitive_conversions/str.sw @@ -0,0 +1,38 @@ +library; + +use ::option::Option::{self, *}; + +impl str { + pub fn try_as_str_array(self) -> Option { + __assert_is_str_array::(); + let str_size = __size_of_str_array::(); + let source = self.as_ptr(); + + if self.len() == str_size { + let s: S = asm(str_size: str_size, source: source, dest) { + move dest sp; + cfe str_size; + mcp dest source str_size; + dest: S + }; + asm (str_size: str_size) { + cfs str_size; + } + Some(s) + } else { + None + } + } +} + +#[test] +fn str_slice_to_str_array() { + use ::assert::*; + use core::str::*; + + let a = "abcd"; + let b: str[4] = a.try_as_str_array().unwrap(); + let c = from_str_array(b); + + assert(a == c); +} diff --git a/sway-lib-std/src/primitive_conversions/u16.sw b/sway-lib-std/src/primitive_conversions/u16.sw new file mode 100644 index 00000000000..1c3a0aba42f --- /dev/null +++ b/sway-lib-std/src/primitive_conversions/u16.sw @@ -0,0 +1,96 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl u16 { + pub fn try_as_u8(self) -> Option { + if self <= u8::max().as_u16() { + Some(asm(input: self) { + input: u8 + }) + } else { + None + } + } +} + +impl TryFrom for u16 { + fn try_from(u: u32) -> Option { + if u > u16::max().as_u32() { + None + } else { + Some(asm(r1: u) {r1: u16}) + } + } +} + +impl TryFrom for u16 { + fn try_from(u: u64) -> Option { + if u > u16::max().as_u64() { + None + } else { + Some(asm(r1: u) {r1: u16}) + } + } +} + +impl TryFrom for u16 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 || parts.3 > u16::max().as_u64() { + None + } else { + Some(asm(r1: parts.3) {r1: u16}) + } + } +} + +#[test] +fn test_u16_try_from_u32() { + use ::assert::assert; + + let u32_1: u32 = 2u32; + let u32_2: u32 = u16::max().as_u32() + 1; + + let u16_1 = >::try_from(u32_1); + let u16_2 = >::try_from(u32_2); + + assert(u16_1.is_some()); + assert(u16_1.unwrap() == 2u16); + + assert(u16_2.is_none()); +} + +#[test] +fn test_u16_try_from_u64() { + use ::assert::assert; + + let u64_1: u64 = 2; + let u64_2: u64 = u16::max().as_u64() + 1; + + let u16_1 = >::try_from(u64_1); + let u16_2 = >::try_from(u64_2); + + assert(u16_1.is_some()); + assert(u16_1.unwrap() == 2u16); + + assert(u16_2.is_none()); +} + +#[test] +fn test_u16_try_from_u256() { + use ::assert::assert; + + let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2: u256 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u16_1 = >::try_from(u256_1); + let u16_2 = >::try_from(u256_2); + + assert(u16_1.is_some()); + assert(u16_1.unwrap() == 2u16); + + assert(u16_2.is_none()); +} diff --git a/sway-lib-std/src/primitive_conversions/u32.sw b/sway-lib-std/src/primitive_conversions/u32.sw new file mode 100644 index 00000000000..fe6c33cf33c --- /dev/null +++ b/sway-lib-std/src/primitive_conversions/u32.sw @@ -0,0 +1,81 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl u32 { + pub fn try_as_u8(self) -> Option { + if self <= u8::max().as_u32() { + Some(asm(input: self) { + input: u8 + }) + } else { + None + } + } + + pub fn try_as_u16(self) -> Option { + if self <= u16::max().as_u32() { + Some(asm(input: self) { + input: u16 + }) + } else { + None + } + } +} + + +impl TryFrom for u32 { + fn try_from(u: u64) -> Option { + if u > u32::max().as_u64() { + None + } else { + Some(asm(r1: u) {r1: u32}) + } + } +} + +impl TryFrom for u32 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 || parts.3 > u32::max().as_u64() { + None + } else { + Some(asm(r1: parts.3) {r1: u32}) + } + } +} + +#[test] +fn test_u32_try_from_u64() { + use ::assert::assert; + + let u64_1: u64 = 2; + let u64_2: u64 = u32::max().as_u64() + 1; + + let u32_1 = >::try_from(u64_1); + let u32_2 = >::try_from(u64_2); + + assert(u32_1.is_some()); + assert(u32_1.unwrap() == 2u32); + + assert(u32_2.is_none()); +} + +#[test] +fn test_u32_try_from_u256() { + use ::assert::assert; + + let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2: u256 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u32_1 = >::try_from(u256_1); + let u32_2 = >::try_from(u256_2); + + assert(u32_1.is_some()); + assert(u32_1.unwrap() == 2u32); + + assert(u32_2.is_none()); +} diff --git a/sway-lib-std/src/primitive_conversions/u64.sw b/sway-lib-std/src/primitive_conversions/u64.sw new file mode 100644 index 00000000000..c7745fc778e --- /dev/null +++ b/sway-lib-std/src/primitive_conversions/u64.sw @@ -0,0 +1,64 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl u64 { + pub fn try_as_u8(self) -> Option { + if self <= u8::max().as_u64() { + Some(asm(input: self) { + input: u8 + }) + } else { + None + } + } + + pub fn try_as_u16(self) -> Option { + if self <= u16::max().as_u64() { + Some(asm(input: self) { + input: u16 + }) + } else { + None + } + } + + pub fn try_as_u32(self) -> Option { + if self <= u32::max().as_u64() { + Some(asm(input: self) { + input: u32 + }) + } else { + None + } + } +} + +impl TryFrom for u64 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 { + None + } else { + Some(parts.3) + } + } +} + +#[test] +fn test_u64_try_from_u256() { + use ::assert::assert; + + let u256_1 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u64_1 = u64::try_from(u256_1); + let u64_2 = u64::try_from(u256_2); + + assert(u64_1.is_some()); + assert(u64_1.unwrap() == 2); + + assert(u64_2.is_none()); +} diff --git a/sway-lib-std/src/primitive_conversions/u8.sw b/sway-lib-std/src/primitive_conversions/u8.sw new file mode 100644 index 00000000000..e1ef5d86b9f --- /dev/null +++ b/sway-lib-std/src/primitive_conversions/u8.sw @@ -0,0 +1,110 @@ +library; + +use ::convert::TryFrom; +use ::option::Option::{self, *}; + +impl TryFrom for u8 { + fn try_from(u: u16) -> Option { + if u > u8::max().as_u16() { + None + } else { + Some(asm(r1: u) {r1: u8}) + } + } +} + +impl TryFrom for u8 { + fn try_from(u: u32) -> Option { + if u > u8::max().as_u32() { + None + } else { + Some(asm(r1: u) {r1: u8}) + } + } +} + +impl TryFrom for u8 { + fn try_from(u: u64) -> Option { + if u > u8::max().as_u64() { + None + } else { + Some(asm(r1: u) {r1: u8}) + } + } +} + +impl TryFrom for u8 { + fn try_from(u: u256) -> Option { + let parts = asm(r1: u) { r1: (u64, u64, u64, u64) }; + + if parts.0 != 0 || parts.1 != 0 || parts.2 != 0 || parts.3 > u8::max().as_u64() { + None + } else { + Some(asm(r1: parts.3) {r1: u8}) + } + } +} + +#[test] +fn test_u8_try_from_u16() { + use ::assert::assert; + + let u16_1: u16 = 2u16; + let u16_2: u16 = u8::max().as_u16() + 1; + + let u8_1 = >::try_from(u16_1); + let u8_2 = >::try_from(u16_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} + +#[test] +fn test_u8_try_from_u32() { + use ::assert::assert; + + let u32_1: u32 = 2u32; + let u32_2: u32 = u16::max().as_u32() + 1; + + let u8_1 = >::try_from(u32_1); + let u8_2 = >::try_from(u32_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} + +#[test] +fn test_u8_try_from_u64() { + use ::assert::assert; + + let u64_1: u64 = 2; + let u64_2: u64 = u16::max().as_u64() + 1; + + let u8_1 = >::try_from(u64_1); + let u8_2 = >::try_from(u64_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} + +#[test] +fn test_u8_try_from_u256() { + use ::assert::assert; + + let u256_1: u256 = 0x0000000000000000000000000000000000000000000000000000000000000002u256; + let u256_2: u256 = 0x1000000000000000000000000000000000000000000000000000000000000000u256; + + let u8_1 = >::try_from(u256_1); + let u8_2 = >::try_from(u256_2); + + assert(u8_1.is_some()); + assert(u8_1.unwrap() == 2u8); + + assert(u8_2.is_none()); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/integer_type_inference/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/integer_type_inference/src/main.sw index 808acf132f2..af81c1353d3 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/integer_type_inference/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/integer_type_inference/src/main.sw @@ -1,5 +1,7 @@ script; +use std::primitive_conversions::{u16::*, u32::*, u64::*}; + /* Test Constants */ const X1: u8 = 4u8; const X2: u8 = 4; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw index ffdf6ebd0b4..ea39bf95f0f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/src/main.sw @@ -1,5 +1,7 @@ contract; +use std::primitive_conversions::{u32::*, u64::*}; + abi U128Contract { fn multiply_u64(a: u64, b: u64) -> (u64, u64); } diff --git a/test/src/sdk-harness/test_projects/token_ops/src/main.sw b/test/src/sdk-harness/test_projects/token_ops/src/main.sw index 56c51676a65..0008cc55a79 100644 --- a/test/src/sdk-harness/test_projects/token_ops/src/main.sw +++ b/test/src/sdk-harness/test_projects/token_ops/src/main.sw @@ -1,6 +1,6 @@ contract; -use std::{contract_id::AssetId, bytes::Bytes, constants::ZERO_B256, context::balance_of, message::send_message, token::*}; +use std::{contract_id::AssetId, bytes::Bytes, constants::ZERO_B256, context::balance_of, message::send_message, primitive_conversions::u64::*, token::*}; abi TestFuelCoin { fn mint_coins(mint_amount: u64, sub_id: b256);