Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 43 additions & 18 deletions sway-lib-std/src/vec.sw
Original file line number Diff line number Diff line change
Expand Up @@ -822,17 +822,35 @@ where
false
}
fn abi_encode(self, buffer: Buffer) -> Buffer {
let len = self.len;
let mut buffer = len.abi_encode(buffer);
const IS_ELEM_TRIVIAL = is_encode_trivial::<T>();

if IS_ELEM_TRIVIAL {
// TODO: We need to write the length of the vector,
// and then the raw memory of the vector's contents.
// Currently, we don't have a way to write the plain
// memory content without the length. E.g., writing slice
// will write the length of the vector slice in bytes,
// and then the raw memory, but not the length of the
// vector in number of elements.
//
// One possibility to solve this would be to have an intrinsic
// that would allow us to write the raw memory of a particular
// length, without the length prefix. E.g.:
// __encode_buffer_append_raw(buffer, ptr, len)
self.as_raw_slice().abi_encode(buffer)
} else {
let len = self.len;
let mut buffer = len.abi_encode(buffer);

let mut i = 0;
while i < len {
let item = self.get_unchecked(i);
buffer = item.abi_encode(buffer);
i += 1;
}

let mut i = 0;
while i < len {
let item = self.get_unchecked(i);
buffer = item.abi_encode(buffer);
i += 1;
buffer
}

buffer
}
}

Expand All @@ -844,18 +862,25 @@ where
false
}
fn abi_decode(ref mut buffer: BufferReader) -> Vec<T> {
let len = u64::abi_decode(buffer);
const IS_ELEM_TRIVIAL = is_decode_trivial::<T>();

let mut v = Vec::with_capacity(len);
if IS_ELEM_TRIVIAL {
// TODO: See the comment in `abi_encode` for the trivial case.
Self::from(raw_slice::abi_decode(buffer))
} else {
let len = u64::abi_decode(buffer);

let mut i = 0;
while i < len {
let item = T::abi_decode(buffer);
v.push(item);
i += 1;
}
let mut v = Vec::with_capacity(len);

let mut i = 0;
while i < len {
let item = T::abi_decode(buffer);
v.push(item);
i += 1;
}

v
v
}
}
}

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[[package]]
name = "raw_ptr_ret"
source = "member"
dependencies = ["std"]
name = "std"
source = "path+from-root-2158C659FB34C0B1"

[[package]]
name = "std"
source = "path+from-root-51D3AF48CE04FAE0"
name = "vec_encoding_decoding"
source = "member"
dependencies = ["std"]
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "vec_ret"
name = "vec_encoding_decoding"

[dependencies]
std = { path = "../../../../../reduced_std_libs/sway-lib-std-vec" }
std = { path = "../../../../reduced_std_libs/sway-lib-std-vec" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cmds = ["forc test --path {root} --release --logs"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
script;

fn main(trivial: Vec<u64>, non_trivial: Vec<u32>) -> (Vec<u64>, Vec<u32>) {
assert_eq(trivial.len(), 3);
assert_eq(trivial.get(0).unwrap_or(0), 124);
assert_eq(trivial.get(1).unwrap_or(0), 124);
assert_eq(trivial.get(2).unwrap_or(0), 124);

let mut trivial = Vec::from(trivial.as_raw_slice());
trivial.push(124);
trivial.push(124);
trivial.push(124);

assert_eq(non_trivial.len(), 3);
assert_eq(non_trivial.get(0).unwrap_or(0), 124);
assert_eq(non_trivial.get(1).unwrap_or(0), 124);
assert_eq(non_trivial.get(2).unwrap_or(0), 124);

let mut non_trivial = Vec::from(non_trivial.as_raw_slice());
non_trivial.push(124);
non_trivial.push(124);
non_trivial.push(124);

(trivial, non_trivial)
}

#[test]
fn vec_trivial() {
let vec = create_vec_trivial(10);
let encoded_decoded: Vec<u64> = abi_decode(encode(vec));
assert_eq(encoded_decoded, vec);

let encoded_as_alias = encode_allow_alias(&encoded_decoded);
let encoded_decoded: Vec<u64> = abi_decode(encoded_as_alias);
assert_eq(encoded_decoded, vec);

log(encoded_decoded);
}

#[test]
fn nested_vec_trivial() {
let vec = create_nested_vec_trivial(10);
let encoded_decoded: Vec<Vec<u64>> = abi_decode(encode(vec));
assert_eq(encoded_decoded, vec);

let encoded_as_alias = encode_allow_alias(&encoded_decoded);
let encoded_decoded: Vec<Vec<u64>> = abi_decode(encoded_as_alias);
assert_eq(encoded_decoded, vec);

log(encoded_decoded);
}

#[test]
fn vec_non_trivial() {
let vec = create_vec_trivial(10);
let encoded_decoded: Vec<u64> = abi_decode(encode(vec));
assert_eq(encoded_decoded, vec);

let encoded_as_alias = encode_allow_alias(&encoded_decoded);
let encoded_decoded: Vec<u64> = abi_decode(encoded_as_alias);
assert_eq(encoded_decoded, vec);

log(encoded_decoded);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test vec_non_trivial incorrectly tests trivial path instead

Medium Severity

The vec_non_trivial test calls create_vec_trivial(10) and uses Vec<u64>, making it an exact duplicate of vec_trivial. It was clearly intended to call create_vec_non_trivial(10) and use Vec<u32> (the non-trivial type, since u32 has is_encode_trivial() -> false). This means the non-trivial encoding/decoding path for flat Vec<u32> has no unit test coverage. Compare with nested_vec_non_trivial which correctly uses create_nested_vec_non_trivial and Vec<Vec<u32>>.

Fix in Cursor Fix in Web

}

#[test]
fn nested_vec_non_trivial() {
let vec = create_nested_vec_non_trivial(10);
let encoded_decoded: Vec<Vec<u32>> = abi_decode(encode(vec));
assert_eq(encoded_decoded, vec);

let encoded_as_alias = encode_allow_alias(&encoded_decoded);
let encoded_decoded: Vec<Vec<u32>> = abi_decode(encoded_as_alias);
assert_eq(encoded_decoded, vec);

log(encoded_decoded);
}

#[allow(dead_code)]
fn create_vec_trivial(n: u64) -> Vec<u64> {
let mut vec = Vec::<u64>::new();
let mut i: u64 = 0;
while i < n {
vec.push(i);
i += 1;
}
vec
}

#[allow(dead_code)]
fn create_nested_vec_trivial(n: u64) -> Vec<Vec<u64>> {
let mut vec = Vec::<Vec<u64>>::new();
let mut i: u64 = 0;
while i < n {
vec.push(create_vec_trivial(i));
i += 1;
}
vec
}

#[allow(dead_code)]
fn create_vec_non_trivial(n: u32) -> Vec<u32> {
let mut vec = Vec::<u32>::new();
let mut i: u32 = 0;
while i < n {
vec.push(i);
i += 1;
}
vec
}

#[allow(dead_code)]
fn create_nested_vec_non_trivial(n: u32) -> Vec<Vec<u32>> {
let mut vec = Vec::<Vec<u32>>::new();
let mut i: u32 = 0;
while i < n {
vec.push(create_vec_non_trivial(i));
i += 1;
}
vec
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
category = "run"

script_data_new_encoding = "0000000000000003000000000000007c000000000000007c000000000000007c 00000000000000030000007c0000007c0000007c"
expected_result_new_encoding = { action = "return_data", value = "0000000000000006000000000000007c000000000000007c000000000000007c000000000000007c000000000000007c000000000000007c 00000000000000060000007c0000007c0000007c0000007c0000007c0000007c" }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Script test data incompatible with new trivial encoding format

High Severity

The script_data_new_encoding encodes Vec<u64> with an element-count length prefix (0000000000000003 = 3), but the new trivial decode path uses raw_slice::abi_decode which interprets the prefix as a byte count. For 3 u64 elements the byte count needs to be 24 (0x18). Reading only 3 bytes produces a Vec with len = 3/8 = 0, causing the assert_eq(trivial.len(), 3) in main to fail. The expected_result_new_encoding has the same mismatch (uses 6 instead of 48).

Fix in Cursor Fix in Web

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
category = "unit_tests_pass"
Loading