Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add Clone implementation for String and Vec #6865

Merged
merged 5 commits into from
Jan 29, 2025
Merged
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
54 changes: 0 additions & 54 deletions sway-lib-std/src/bytes.sw
Original file line number Diff line number Diff line change
Expand Up @@ -1020,57 +1020,3 @@ impl AbiDecode for Bytes {
raw_slice::abi_decode(buffer).into()
}
}

#[test]
fn ok_bytes_buffer_ownership() {
let mut original_array = [1u8, 2u8, 3u8, 4u8];
let slice = raw_slice::from_parts::<u8>(__addr_of(original_array), 4);

// Check Bytes duplicates the original slice
let mut bytes = Bytes::from(slice);
bytes.set(0, 5);
assert(original_array[0] == 1);

// At this point, slice equals [5, 2, 3, 4]
let encoded_slice = encode(bytes);

// `Bytes` should duplicate the underlying buffer,
// so when we write to it, it should not change
// `encoded_slice`
let mut bytes = abi_decode::<Bytes>(encoded_slice);
bytes.set(0, 6);
assert(bytes.get(0) == Some(6));

let mut bytes = abi_decode::<Bytes>(encoded_slice);
assert(bytes.get(0) == Some(5));
}

#[test]
fn ok_bytes_bigger_than_3064() {
let mut v: Bytes = Bytes::new();

// We allocate 1024 bytes initially, this is throw away because
// it is not big enough for the buffer.
// Then we used to double the buffer to 2048.
// Then we write an `u64` with the length of the buffer.
// Then we write the buffer itself.
// (1024 + 2048) - 8 = 3064
// Thus, we need a buffer with 3065 bytes to write into the red zone
let mut a = 3065;
while a > 0 {
v.push(1u8);
a -= 1;
}

// This red zone should not be overwritten
let red_zone = asm(size: 1024) {
aloc size;
hp: raw_ptr
};
red_zone.write(0xFFFFFFFFFFFFFFFF);
assert(red_zone.read::<u64>() == 0xFFFFFFFFFFFFFFFF);

let _ = encode(v);

assert(red_zone.read::<u64>() == 0xFFFFFFFFFFFFFFFF);
}
1 change: 1 addition & 0 deletions sway-lib-std/src/prelude.sw
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub use ::revert::{require, revert, revert_with_log};

// Convert
pub use ::convert::From;
pub use ::clone::Clone;

// Primitive conversions
pub use ::primitive_conversions::{b256::*, str::*, u16::*, u256::*, u32::*, u64::*, u8::*};
Expand Down
9 changes: 9 additions & 0 deletions sway-lib-std/src/string.sw
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ::bytes::*;
use ::convert::*;
use ::hash::{Hash, Hasher};
use ::option::Option;
use ::clone::Clone;

/// A UTF-8 encoded growable string. It has ownership over its buffer.
///
Expand Down Expand Up @@ -341,3 +342,11 @@ impl AbiDecode for String {
}
}
}

impl Clone for String {
fn clone(self) -> Self {
Self {
bytes: self.bytes.clone(),
}
}
}
32 changes: 10 additions & 22 deletions sway-lib-std/src/vec.sw
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ::assert::assert;
use ::option::Option::{self, *};
use ::convert::From;
use ::iterator::*;
use ::clone::Clone;

struct RawVec<T> {
ptr: raw_ptr,
Expand Down Expand Up @@ -791,26 +792,13 @@ impl<T> Iterator for VecIter<T> {
}
}

#[test]
fn ok_vec_buffer_ownership() {
let mut original_array = [1u8, 2u8, 3u8, 4u8];
let slice = raw_slice::from_parts::<u8>(__addr_of(original_array), 4);

// Check Vec duplicates the original slice
let mut bytes = Vec::<u8>::from(slice);
bytes.set(0, 5);
assert(original_array[0] == 1);

// At this point, slice equals [5, 2, 3, 4]
let encoded_slice = encode(bytes);

// `Vec<u8>` should duplicate the underlying buffer,
// so when we write to it, it should not change
// `encoded_slice`
let mut bytes = abi_decode::<Vec<u8>>(encoded_slice);
bytes.set(0, 6);
assert(bytes.get(0) == Some(6));

let mut bytes = abi_decode::<Vec<u8>>(encoded_slice);
assert(bytes.get(0) == Some(5));
impl<T> Clone for Vec<T> {
fn clone(self) -> Self {
let len = self.len();
let buf = RawVec::with_capacity(len);
if len > 0 {
self.ptr().copy_to::<T>(buf.ptr(), len);
}
Self { buf, len }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ revert.sw
vec.sw
iterator.sw
convert.sw
clone.sw
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod assert;
pub mod convert;
pub mod alloc;
pub mod iterator;
pub mod clone;
pub mod vec;

pub mod prelude;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub use ::revert::{require, revert};

// Convert
pub use ::convert::From;
pub use ::clone::Clone;

// Logging
pub use ::logging::log;
Original file line number Diff line number Diff line change
Expand Up @@ -1024,12 +1024,66 @@ fn bytes_clone() {
assert(cloned_bytes.ptr() != bytes.ptr());
assert(cloned_bytes.len() == bytes.len());
// Capacity is not cloned
// assert(cloned_bytes.capacity() == bytes.capacity());
assert(cloned_bytes.capacity() != bytes.capacity());
assert(cloned_bytes.get(0).unwrap() == bytes.get(0).unwrap());
assert(cloned_bytes.get(1).unwrap() == bytes.get(1).unwrap());
assert(cloned_bytes.get(2).unwrap() == bytes.get(2).unwrap());
}

#[test]
fn bytes_buffer_ownership() {
let mut original_array = [1u8, 2u8, 3u8, 4u8];
let slice = raw_slice::from_parts::<u8>(__addr_of(original_array), 4);

// Check Bytes duplicates the original slice
let mut bytes = Bytes::from(slice);
bytes.set(0, 5);
assert(original_array[0] == 1);

// At this point, slice equals [5, 2, 3, 4]
let encoded_slice = encode(bytes);

// `Bytes` should duplicate the underlying buffer,
// so when we write to it, it should not change
// `encoded_slice`
let mut bytes = abi_decode::<Bytes>(encoded_slice);
bytes.set(0, 6);
assert(bytes.get(0) == Some(6));

let mut bytes = abi_decode::<Bytes>(encoded_slice);
assert(bytes.get(0) == Some(5));
}

#[test]
fn bytes_bigger_than_3064() {
let mut v: Bytes = Bytes::new();

// We allocate 1024 bytes initially, this is throw away because
// it is not big enough for the buffer.
// Then we used to double the buffer to 2048.
// Then we write an `u64` with the length of the buffer.
// Then we write the buffer itself.
// (1024 + 2048) - 8 = 3064
// Thus, we need a buffer with 3065 bytes to write into the red zone
let mut a = 3065;
while a > 0 {
v.push(1u8);
a -= 1;
}

// This red zone should not be overwritten
let red_zone = asm(size: 1024) {
aloc size;
hp: raw_ptr
};
red_zone.write(0xFFFFFFFFFFFFFFFF);
assert(red_zone.read::<u64>() == 0xFFFFFFFFFFFFFFFF);

let _ = encode(v);

assert(red_zone.read::<u64>() == 0xFFFFFFFFFFFFFFFF);
}

#[test]
pub fn test_encode_decode() {
let initial = 0x3333333333333333333333333333333333333333333333333333333333333333;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,16 @@ fn string_test_abi_encoding() {

assert(string == decoded_string);
}

#[test]
fn string_clone() {
let string = String::from_ascii_str("fuel");

let cloned_string = string.clone();

assert(cloned_string.ptr() != string.ptr());
assert(cloned_string.as_bytes().len() == string.as_bytes().len());
assert(cloned_string.as_bytes().get(0).unwrap() == string.as_bytes().get(0).unwrap());
assert(cloned_string.as_bytes().get(1).unwrap() == string.as_bytes().get(1).unwrap());
assert(cloned_string.as_bytes().get(2).unwrap() == string.as_bytes().get(2).unwrap());
}
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,45 @@ fn vec_raw_slice_into() {
assert(vec.len() == slice.len::<u64>());
}

#[test]
fn vec_clone() {
let (mut vec, _a, _b, _c) = setup();

let cloned_vec = vec.clone();

assert(cloned_vec.ptr() != vec.ptr());
assert(cloned_vec.len() == vec.len());
// Capacity is not cloned
assert(cloned_vec.capacity() != vec.capacity());
assert(cloned_vec.get(0).unwrap() == vec.get(0).unwrap());
assert(cloned_vec.get(1).unwrap() == vec.get(1).unwrap());
assert(cloned_vec.get(2).unwrap() == vec.get(2).unwrap());
}

#[test]
fn vec_buffer_ownership() {
let mut original_array = [1u8, 2u8, 3u8, 4u8];
let slice = raw_slice::from_parts::<u8>(__addr_of(original_array), 4);

// Check Vec duplicates the original slice
let mut bytes = Vec::<u8>::from(slice);
bytes.set(0, 5);
assert(original_array[0] == 1);

// At this point, slice equals [5, 2, 3, 4]
let encoded_slice = encode(bytes);

// `Vec<u8>` should duplicate the underlying buffer,
// so when we write to it, it should not change
// `encoded_slice`
let mut bytes = abi_decode::<Vec<u8>>(encoded_slice);
bytes.set(0, 6);
assert(bytes.get(0) == Some(6));

let mut bytes = abi_decode::<Vec<u8>>(encoded_slice);
assert(bytes.get(0) == Some(5));
}

#[test()]
fn vec_encode_and_decode() {
let mut v1: Vec<u64> = Vec::new();
Expand Down
Loading