diff --git a/sway-lib-std/src/vec.sw b/sway-lib-std/src/vec.sw index f2cb34ea1b8..85dd986ced1 100644 --- a/sway-lib-std/src/vec.sw +++ b/sway-lib-std/src/vec.sw @@ -208,4 +208,32 @@ impl Vec { self.len -= 1; Option::Some(read(self.buf.ptr() + self.len * size_of::())) } + + /// Swaps two elements. + /// + /// # Arguments + /// + /// * element1_index - The index of the first element + /// * element2_index - The index of the second element + /// + /// # Reverts + /// + /// Reverts if `element1_index` or `element2_index` is greater than or equal to the length of vector. + pub fn swap(mut self, element1_index: u64, element2_index: u64) { + assert(element1_index < self.len); + assert(element2_index < self.len); + + if element1_index == element2_index { + return; + } + + let val_size = size_of::(); + + let element1_ptr = self.buf.ptr() + element1_index * val_size; + let element2_ptr = self.buf.ptr() + element2_index * val_size; + + let element1_val = read(element1_ptr); + copy(element2_ptr, element1_ptr, val_size); + write(element2_ptr, element1_val); + } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/Forc.lock new file mode 100644 index 00000000000..7e76cc4ddd2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-F443649679DC6856' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-F443649679DC6856' +dependencies = ['core'] + +[[package]] +name = 'vec_swap_param1_out_of_bounds' +source = 'root' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/Forc.toml new file mode 100644 index 00000000000..33f5f75fec6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "vec_swap_param1_out_of_bounds" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/src/main.sw new file mode 100644 index 00000000000..818b5eae915 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/src/main.sw @@ -0,0 +1,21 @@ +script; + +use std::{assert::assert, vec::Vec}; + +fn main() { + let mut vector = ~Vec::new(); + + let number0 = 0u8; + let number1 = 1u8; + let number2 = 2u8; + + vector.push(number0); + vector.push(number1); + vector.push(number2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + vector.swap(3, 0); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/test.toml new file mode 100644 index 00000000000..2f3b260911b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param1_out_of_bounds/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "revert", value = 0 } +validate_abi = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/Forc.lock new file mode 100644 index 00000000000..bc1ac684a36 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-626C5D02EBFA3B6A' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-626C5D02EBFA3B6A' +dependencies = ['core'] + +[[package]] +name = 'vec_swap_param2_out_of_bounds' +source = 'root' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/Forc.toml new file mode 100644 index 00000000000..264e92561d2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "vec_swap_param2_out_of_bounds" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/src/main.sw new file mode 100644 index 00000000000..ec1af4da86a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/src/main.sw @@ -0,0 +1,21 @@ +script; + +use std::{assert::assert, vec::Vec}; + +fn main() { + let mut vector = ~Vec::new(); + + let number0 = 0u8; + let number1 = 1u8; + let number2 = 2u8; + + vector.push(number0); + vector.push(number1); + vector.push(number2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + vector.swap(0, 3); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/test.toml new file mode 100644 index 00000000000..2f3b260911b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec_swap_param2_out_of_bounds/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "revert", value = 0 } +validate_abi = false diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/Forc.lock new file mode 100644 index 00000000000..d3be47664bc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-E670B7A81773E7E0' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-E670B7A81773E7E0' +dependencies = ['core'] + +[[package]] +name = 'vec_swap' +source = 'root' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/Forc.toml new file mode 100644 index 00000000000..ffc31fc5097 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "vec_swap" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/src/main.sw new file mode 100644 index 00000000000..30a0251b9b3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/src/main.sw @@ -0,0 +1,710 @@ +script; + +use std::{assert::assert, hash::sha256, option::Option, revert::revert, u128::{From, U128}, vec::Vec}; + +enum SimpleEnum { + A: b256, + B: (), +} + +struct SimpleStruct { + x: u32, + y: b256, +} + +fn main() -> bool { + test_vector_swap_u8(); + test_vector_swap_b256(); + test_vector_swap_struct(); + test_vector_swap_enum(); + test_vector_swap_tuple(); + test_vector_swap_string(); + test_vector_swap_array(); + test_vector_swap_same_indexes_noop(); + true +} + +fn test_vector_swap_u8() { + let mut vector = ~Vec::new(); + + let number0 = 0u8; + let number1 = 1u8; + let number2 = 2u8; + + vector.push(number0); + vector.push(number1); + vector.push(number2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val == number0) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val == number1) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val == number2) + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val == number2) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val == number1) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val == number0) + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_b256() { + let mut vector = ~Vec::new(); + + let b0 = 0x0000000000000000000000000000000000000000000000000000000000000000; + let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; + let b2 = 0x0000000000000000000000000000000000000000000000000000000000000002; + + vector.push(b0); + vector.push(b1); + vector.push(b2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val == b0) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val == b1) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val == b2) + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val == b2) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val == b1) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val == b0) + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_struct() { + let mut vector = ~Vec::new(); + + let number0 = 0u32; + let number1 = 1u32; + let number2 = 2u32; + + let b0 = 0x0000000000000000000000000000000000000000000000000000000000000000; + let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; + let b2 = 0x0000000000000000000000000000000000000000000000000000000000000002; + + vector.push(SimpleStruct { + x: number0, y: b0 + }); + vector.push(SimpleStruct { + x: number1, y: b1 + }); + vector.push(SimpleStruct { + x: number2, y: b2 + }); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val.x == number0); + assert(val.y == b0); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val.x == number1); + assert(val.y == b1); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val.x == number2); + assert(val.y == b2); + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val.x == number2); + assert(val.y == b2); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val.x == number1); + assert(val.y == b1); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val.x == number0); + assert(val.y == b0); + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_enum() { + let mut vector = ~Vec::new(); + + let b0 = 0x0000000000000000000000000000000000000000000000000000000000000000; + let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; + + vector.push(SimpleEnum::A(b0)); + vector.push(SimpleEnum::A(b1)); + vector.push(SimpleEnum::B); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + match val { + SimpleEnum::A(b) => { + assert(b == b0) + }, + _ => { + revert(0) + }, + } + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + match val { + SimpleEnum::A(b) => { + assert(b == b1) + }, + _ => { + revert(0) + }, + } + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + match val { + SimpleEnum::B => { + }, + _ => { + revert(0) + }, + } + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + match val { + SimpleEnum::B => { + }, + _ => { + revert(0) + }, + } + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + match val { + SimpleEnum::A(b) => { + assert(b == b1) + }, + _ => { + revert(0) + }, + } + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + match val { + SimpleEnum::A(b) => { + assert(b == b0) + }, + _ => { + revert(0) + }, + } + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_tuple() { + let mut vector = ~Vec::new(); + + let number0 = 0u16; + let number1 = 1u16; + let number2 = 2u16; + + let b0 = 0x0000000000000000000000000000000000000000000000000000000000000000; + let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; + let b2 = 0x0000000000000000000000000000000000000000000000000000000000000002; + + vector.push((number0, b0)); + vector.push((number1, b1)); + vector.push((number2, b2)); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val.0 == number0); + assert(val.1 == b0); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val.0 == number1); + assert(val.1 == b1); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val.0 == number2); + assert(val.1 == b2); + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val.0 == number2); + assert(val.1 == b2); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val.0 == number1); + assert(val.1 == b1); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val.0 == number0); + assert(val.1 == b0); + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_string() { + let mut vector = ~Vec::new(); + + let s0 = "fuel"; + let s1 = "john"; + let s2 = "nick"; + + vector.push(s0); + vector.push(s1); + vector.push(s2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(sha256(val) == sha256(s0)); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(sha256(val) == sha256(s1)); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(sha256(val) == sha256(s2)); + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(sha256(val) == sha256(s2)); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(sha256(val) == sha256(s1)); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(sha256(val) == sha256(s0)); + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_array() { + let mut vector = ~Vec::new(); + + let a0 = [0, 1, 2]; + let a1 = [3, 4, 5]; + let a2 = [6, 7, 8]; + + vector.push(a0); + vector.push(a1); + vector.push(a2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val[0] == a0[0]); + assert(val[1] == a0[1]); + assert(val[2] == a0[2]); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val[0] == a1[0]); + assert(val[1] == a1[1]); + assert(val[2] == a1[2]); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val[0] == a2[0]); + assert(val[1] == a2[1]); + assert(val[2] == a2[2]); + }, + Option::None => { + revert(0) + }, + } + + vector.swap(0, 2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val[0] == a2[0]); + assert(val[1] == a2[1]); + assert(val[2] == a2[2]); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val[0] == a1[0]); + assert(val[1] == a1[1]); + assert(val[2] == a1[2]); + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val[0] == a0[0]); + assert(val[1] == a0[1]); + assert(val[2] == a0[2]); + }, + Option::None => { + revert(0) + }, + } +} + +fn test_vector_swap_same_indexes_noop() { + let mut vector = ~Vec::new(); + + let number0 = 0u8; + let number1 = 1u8; + let number2 = 2u8; + + vector.push(number0); + vector.push(number1); + vector.push(number2); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val == number0) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val == number1) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val == number2) + }, + Option::None => { + revert(0) + }, + } + + vector.swap(1, 1); + + assert(vector.len() == 3); + assert(vector.capacity() == 4); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => { + assert(val == number0) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(1) { + Option::Some(val) => { + assert(val == number1) + }, + Option::None => { + revert(0) + }, + } + + match vector.get(2) { + Option::Some(val) => { + assert(val == number2) + }, + Option::None => { + revert(0) + }, + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/test.toml new file mode 100644 index 00000000000..78a2c7303f6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec_swap/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = false