Skip to content

Commit 1c3183a

Browse files
SwayStar123bitzoic
andauthored
Implement Iteration for Bytes (#6953)
## Description Adds an iterator to allow for loops over bytes ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: Cameron Carstens <bitzoic.eth@gmail.com>
1 parent 4b99ee5 commit 1c3183a

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed

sway-lib-std/src/bytes.sw

+111
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use ::intrinsics::size_of_val;
77
use ::option::Option::{self, *};
88
use ::convert::{From, Into, *};
99
use ::clone::Clone;
10+
use ::iterator::*;
1011

1112
struct RawBytes {
1213
ptr: raw_ptr,
@@ -871,6 +872,86 @@ impl Bytes {
871872

872873
self.len = new_len;
873874
}
875+
876+
/// Returns an [Iterator] to iterate over this `Bytes`.
877+
///
878+
/// # Returns
879+
///
880+
/// * [BytesIter] - The struct which can be iterated over.
881+
///
882+
/// # Examples
883+
///
884+
/// ```sway
885+
/// fn foo() {
886+
/// let mut bytes = Bytes::new();
887+
/// bytes.push(5_u8);
888+
/// bytes.push(10_u8);
889+
/// bytes.push(15_u8);
890+
///
891+
/// // Get the iterator
892+
/// let iter = bytes.iter();
893+
///
894+
/// assert_eq(5_u8, iter.next().unwrap());
895+
/// assert_eq(10_u8, iter.next().unwrap());
896+
/// assert_eq(15_u8, iter.next().unwrap());
897+
///
898+
/// for elem in bytes.iter() {
899+
/// log(elem);
900+
/// }
901+
/// }
902+
///
903+
/// # Undefined Behavior
904+
///
905+
/// Modifying vector during iteration is a logical error and
906+
/// results in undefined behavior. E.g.:
907+
///
908+
/// ```sway
909+
/// fn foo() {
910+
/// let mut bytes = Bytes::new();
911+
/// bytes.push(5_u8);
912+
/// bytes.push(10_u8);
913+
/// bytes.push(15_u8);
914+
///
915+
/// for elem in bytes.iter() {
916+
/// bytes.push(20_u8); // Modification causes undefined behavior.
917+
/// }
918+
/// }
919+
/// ```
920+
pub fn iter(self) -> BytesIter {
921+
// WARNING: Be aware of caveats of this implementation
922+
// if you take it as an example for implementing
923+
// `Iterator` for other types.
924+
//
925+
// Due to the Sway's copy semantics, the `values` will
926+
// actually contain **a copy of the original bytes
927+
// `self`**. This is contrary to the iterator semantics
928+
// which should iterate over the collection itself.
929+
//
930+
// Strictly speaking, we should take a reference to
931+
// `self` here, but references as for now an experimental
932+
// feature.
933+
//
934+
// However, this issue of copying gets compensated by
935+
// another issue, which is the broken copy semantics
936+
// for heap types like `Bytes`. Essentially, the original
937+
// `self` and it's copy `values` will both point to
938+
// the same elements on the heap, which gives us the
939+
// desired behavior for the iterator.
940+
//
941+
// This fact makes the implementation of `next` very
942+
// misleading in the part where the bytes length is
943+
// checked (see comment in the `next` implementation
944+
// below).
945+
//
946+
// Once we fix and formalize the copying of heap types
947+
// this implementation will be changed, but for
948+
// the time being, it is the most pragmatic one we can
949+
// have now.
950+
BytesIter {
951+
values: self,
952+
index: 0,
953+
}
954+
}
874955
}
875956

876957
#[cfg(experimental_partial_eq = false)]
@@ -1091,3 +1172,33 @@ impl AbiDecode for Bytes {
10911172
raw_slice::abi_decode(buffer).into()
10921173
}
10931174
}
1175+
1176+
pub struct BytesIter {
1177+
values: Bytes,
1178+
index: u64,
1179+
}
1180+
1181+
impl Iterator for BytesIter {
1182+
type Item = u8;
1183+
fn next(ref mut self) -> Option<Self::Item> {
1184+
// BEWARE: `self.values` keeps **the copy** of the `Bytes`
1185+
// we iterate over. The below check checks against
1186+
// the length of that copy, taken when the iterator
1187+
// was created, and not the original vector.
1188+
//
1189+
// If the original vector gets modified during the iteration
1190+
// (e.g., elements are removed), this modification will not
1191+
// be reflected in `self.values.len()`.
1192+
//
1193+
// But since modifying the vector during iteration is
1194+
// considered undefined behavior, this implementation,
1195+
// that always checks against the length at the time
1196+
// the iterator got created is perfectly valid.
1197+
if self.index >= self.values.len() {
1198+
return None
1199+
}
1200+
1201+
self.index += 1;
1202+
self.values.get(self.index - 1)
1203+
}
1204+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
library;
2+
3+
use std::bytes::Bytes;
4+
5+
#[test]
6+
fn bytes_iter() {
7+
let mut bytes: Bytes = Bytes::new();
8+
9+
let number0 = 0;
10+
let number1 = 1;
11+
let number2 = 2;
12+
let number3 = 3;
13+
let number4 = 4;
14+
15+
bytes.push(number0);
16+
bytes.push(number1);
17+
bytes.push(number2);
18+
bytes.push(number3);
19+
bytes.push(number4);
20+
21+
let mut iter = bytes.iter();
22+
23+
assert(iter.next() == Some(number0));
24+
assert(iter.next() == Some(number1));
25+
assert(iter.next() == Some(number2));
26+
assert(iter.next() == Some(number3));
27+
assert(iter.next() == Some(number4));
28+
assert(iter.next() == None);
29+
assert(iter.next() == None);
30+
}
31+
32+
#[test]
33+
fn bytes_for_loop() {
34+
let mut bytes: Bytes = Bytes::new();
35+
36+
let number0 = 0;
37+
let number1 = 1;
38+
let number2 = 2;
39+
let number3 = 3;
40+
let number4 = 4;
41+
42+
bytes.push(number0);
43+
bytes.push(number1);
44+
bytes.push(number2);
45+
bytes.push(number3);
46+
bytes.push(number4);
47+
48+
let arr = [number0, number1, number2, number3, number4];
49+
50+
let mut i = 0;
51+
for elem in bytes.iter() {
52+
assert(elem == arr[i]);
53+
i += 1;
54+
}
55+
}

test/src/in_language_tests/test_programs/bytes_inline_tests/src/main.sw

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ library;
22

33
pub mod utils;
44
pub mod splice;
5+
pub mod iter;
56

67
use utils::setup;
78
use std::bytes::Bytes;

0 commit comments

Comments
 (0)