Skip to content

Commit

Permalink
Bytes: refactor & rename join to append (FuelLabs#3895)
Browse files Browse the repository at this point in the history
This renames both `split` and `join` to `split_at` and `append`, which
is consistent with WIP changes to the Vec library, and the rust naming.
This also updates the behaviour and implementation to be both more
performant(presumably) and consistent with the implementation of the
same functions in the Vec library.
  • Loading branch information
nfurfaro authored Jan 28, 2023
1 parent 8075e95 commit dd8b2f6
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 47 deletions.
195 changes: 151 additions & 44 deletions sway-lib-std/src/bytes.sw
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,9 @@ impl Bytes {
/// assert(bytes.is_empty());
/// ```
pub fn clear(ref mut self) {
self.buf.ptr = alloc_bytes(0);
self.len = 0;
self.buf.cap = 0;
}

/// Returns `true` if the vector contains no elements.
Expand Down Expand Up @@ -551,47 +553,61 @@ impl Bytes {
vec
}

/// Splits a `Bytes` at the given index, modifying the original and returning the right-hand side `Bytes`.
/// Divides one Bytes into two at an index.
///
/// The first will contain all indices from `[0, mid)` (excluding the index
/// `mid` itself) and the second will contain all indices from `[mid, len)`
/// (excluding the index `len` itself).
///
/// ### Arguments
///
/// * index - The index to split the original Bytes at
/// * mid - Index at which the Bytes is to be split
///
/// ### Reverts
///
/// * if `mid > self.len`
///
/// ### Examples
///
/// ```sway
///
/// use std:bytes::Bytes;
///
/// let (mut bytes, a, b, c) = setup();
/// assert(bytes.len() == 3);
/// let index = 1;
/// let (first, second) = bytes.split(index);
/// assert(first.capacity() == index);
/// assert(second.capacity() == bytes.len() - index);
/// assert(first.len() == 1);
/// assert(second.len() == 2);
/// let mid = 1;
/// let (left, right) = bytes.split_at(mid);
/// assert(left.capacity() == mid);
/// assert(right.capacity() == bytes.len() - mid);
/// assert(left.len() == 1);
/// assert(right.len() == 2);
/// ```
pub fn split(ref mut self, index: u64) -> Bytes {
assert(index != 0);
assert(index < self.len - 1);
let mut second = Bytes::with_capacity(self.len - index);
pub fn split_at(self, mid: u64) -> (Bytes, Bytes) {
assert(self.len >= mid);

let mut i = index;
while i < self.len {
second.push(self.get(i).unwrap());
i += 1;
let left_len = mid;
let right_len = self.len - mid;

let mut left_bytes = Self { buf: RawBytes::with_capacity(left_len), len: left_len };
let mut right_bytes = Self { buf: RawBytes::with_capacity(right_len), len: right_len };

if mid > 0 {
self.buf.ptr().copy_bytes_to(left_bytes.buf.ptr(), left_len);
};
if mid != self.len {
self.buf.ptr().add_uint_offset(mid).copy_bytes_to(right_bytes.buf.ptr(), right_len);
};

left_bytes.len = left_len;
right_bytes.len = right_len;

self.len = index;
second
(left_bytes, right_bytes)
}

/// Joins two `Bytes` into a single larger `Bytes`.
/// Moves all elements of `other` into `self`, leaving `other` empty.
///
/// ### Arguments
///
/// * other - The Bytes to join to self.
/// * other - The Bytes to append to self.
///
/// ### Examples
///
Expand All @@ -612,26 +628,45 @@ impl Bytes {
/// bytes2.push(9u8);
/// assert(bytes2.len() == 3);
///
/// let mut joined = bytes.join(bytes2);
/// assert(joined.len() == bytes.len() + bytes2.len());
/// assert(joined.capacity() == bytes.len() + bytes2.len());
/// let first_length = bytes.len();
/// let second_length = bytes2.len();
/// let first_cap = bytes.capacity();
/// let second_cap = bytes2.capacity();
/// bytes.append(bytes2);
/// assert(bytes.len() == first_length + second_length);
/// assert(bytes.capacity() == first_length + first_length);
/// ```
pub fn join(ref mut self, other: self) -> Self {
let mut joined = Bytes::with_capacity(self.len + other.len);
pub fn append(ref mut self, ref other: self) {
if other.len == 0 {
return
};

let mut i = 0;
while i < self.len {
joined.push(self.get(i).unwrap());
i += 1;
// optimization for when starting with empty bytes and appending to it
if self.len == 0 {
self = other;
other.clear();
return;
};

i = 0;
let both_len = self.len + other.len;
let other_start = self.len;

// reallocate with combined capacity, write `other`, set buffer capacity
self.buf.ptr = realloc_bytes(self.buf.ptr(), self.buf.capacity(), both_len);

let mut i = 0;
while i < other.len {
joined.push(other.get(i).unwrap());
let new_ptr = self.buf.ptr().add_uint_offset(other_start);
new_ptr.add_uint_offset(i).write_byte(other.buf.ptr.add_uint_offset(i).read_byte());
i += 1;
};
}

joined
// set capacity and length
self.buf.cap = both_len;
self.len = both_len;

// clear `other`
other.clear();
}
}

Expand Down Expand Up @@ -914,19 +949,43 @@ fn test_bytes_limits() {
}

#[test()]
fn test_split() {
fn test_split_at() {
let (mut original, a, b, c) = setup();
assert(original.len() == 3);
let index = 1;
let second = original.split(index);
let (left, right) = original.split_at(index);
assert(original.capacity() == 4);
assert(right.capacity() == 2);
assert(left.len() == 1);
assert(right.len() == 2);
}

#[test()]
fn test_split_at_0() {
let (mut original, a, b, c) = setup();
assert(original.len() == 3);
let index = 0;
let (left, right) = original.split_at(index);
assert(original.capacity() == 4);
assert(second.capacity() == 2);
assert(original.len() == 1);
assert(second.len() == 2);
assert(right.capacity() == 3);
assert(left.len() == 0);
assert(right.len() == 3);
}

#[test()]
fn test_join() {
fn test_split_at_len() {
let (mut original, a, b, c) = setup();
assert(original.len() == 3);
let index = 3;
let (left, right) = original.split_at(index);
assert(original.capacity() == 4);
assert(right.capacity() == 0);
assert(left.len() == 3);
assert(right.len() == 0);
}

#[test()]
fn test_append() {
let (mut bytes, a, b, c) = setup();
assert(bytes.len() == 3);
assert(bytes.get(0).unwrap() == a);
Expand All @@ -945,17 +1004,65 @@ fn test_join() {
assert(bytes2.get(1).unwrap() == e);
assert(bytes2.get(2).unwrap() == f);

let mut joined = bytes.join(bytes2);
assert(joined.len() == bytes.len() + bytes2.len());
assert(joined.capacity() == bytes.len() + bytes2.len());
let first_length = bytes.len();
let second_length = bytes2.len();
let first_cap = bytes.capacity();
let second_cap = bytes2.capacity();
bytes.append(bytes2);
assert(bytes.len() == first_length + second_length);
assert(bytes.capacity() == first_length + first_length);
let values = [a, b, c, d, e, f];
let mut i = 0;
while i < 6 {
assert(joined.get(i).unwrap() == values[i]);
assert(bytes.get(i).unwrap() == values[i]);
i += 1;
};
}

#[test()]
fn test_append_empty_bytes() {
// nothing is appended or modified when appending an empty bytes.
let (mut bytes, a, b, c) = setup();
assert(bytes.len() == 3);
assert(bytes.get(0).unwrap() == a);
assert(bytes.get(1).unwrap() == b);
assert(bytes.get(2).unwrap() == c);

let mut bytes2 = Bytes::new();
assert(bytes2.len() == 0);
let first_length = bytes.len();
let first_cap = bytes.capacity();
bytes.append(bytes2);
assert(bytes.len() == first_length);
assert(bytes.capacity() == first_cap);
}

#[test()]
fn test_append_to_empty_bytes() {
let mut bytes = Bytes::new();
assert(bytes.len() == 0);
let (mut bytes2, a, b, c) = setup();
assert(bytes2.len() == 3);

let first_length = bytes.len();
let first_cap = bytes.capacity();
let second_length = bytes2.len();
let second_cap = bytes2.capacity();
bytes.append(bytes2);
assert(bytes.len() == second_length);
assert(bytes.capacity() == second_cap);
let values = [a, b, c];
let mut i = 0;
while i < 3 {
assert(bytes.get(i).unwrap() == values[i]);
i += 1;
};

assert(bytes2.len() == 0);
assert(bytes2.capacity() == 0);

}

#[test()]
fn test_eq() {
let (mut bytes, a, b, c) = setup();
Expand Down
9 changes: 6 additions & 3 deletions sway-lib-std/src/low_level_call.sw
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ fn create_payload(
*/
require(function_selector.len() == 8, "function selector must be 8 bytes");

let mut payload = Bytes::new().join(contract_id_to_bytes(target)).join(function_selector);
// let mut payload = Bytes::new().append(contract_id_to_bytes(target)).append(function_selector);
let mut payload = Bytes::new();
payload.append(contract_id_to_bytes(target));
payload.append(function_selector);

if (single_value_type_arg) {
payload = payload.join(calldata); // When calldata is copy type, just pass calldata
payload.append(calldata); // When calldata is copy type, just pass calldata
} else {
payload = payload.join(ptr_as_bytes(calldata.buf.ptr)); // When calldata is reference type, need to get pointer as bytes
payload.append(ptr_as_bytes(calldata.buf.ptr)); // When calldata is reference type, need to get pointer as bytes
};

payload
Expand Down

0 comments on commit dd8b2f6

Please sign in to comment.