Skip to content

Commit 986aee2

Browse files
SwayStar123K1-R1
andauthored
Add splice method to Bytes (#6823)
## Description Adds a splice method to bytes that lets you select start and end indices, and returns the range of bytes, removing them from the original 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). - [ ] 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: K1-R1 <77465250+K1-R1@users.noreply.github.com>
1 parent bdbe0b7 commit 986aee2

File tree

4 files changed

+323
-11
lines changed

4 files changed

+323
-11
lines changed

sway-lib-std/src/bytes.sw

+97
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,103 @@ impl Bytes {
719719
// set capacity and length
720720
self.len = both_len;
721721
}
722+
723+
/// Removes and returns a range of elements from the `Bytes` (i.e. indices `[start, end)`),
724+
/// then replaces that range with the contents of `replace_with`.
725+
///
726+
/// # Arguments
727+
///
728+
/// * `start`: [u64] - The starting index for the splice (inclusive).
729+
/// * `end`: [u64] - The ending index for the splice (exclusive).
730+
/// * `replace_with`: [Bytes] - The elements to insert in place of the removed range.
731+
///
732+
/// # Returns
733+
///
734+
/// * [Bytes] - A new `Bytes` containing all of the elements from `start` up to (but not including) `end`.
735+
///
736+
/// # Reverts
737+
///
738+
/// * When `start > end`.
739+
/// * When `end > self.len`.
740+
///
741+
/// # Examples
742+
///
743+
/// ```sway
744+
/// use std::bytes::Bytes;
745+
///
746+
/// fn foo() {
747+
/// let mut bytes = Bytes::new();
748+
/// bytes.push(5u8); // index 0
749+
/// bytes.push(7u8); // index 1
750+
/// bytes.push(9u8); // index 2
751+
///
752+
/// // Replace the middle item (index 1) with two new items
753+
/// let mut replacement = Bytes::new();
754+
/// replacement.push(42u8);
755+
/// replacement.push(100u8);
756+
///
757+
/// // Splice out range [1..2) => removes the single element 7u8,
758+
/// // then inserts [42, 100] there
759+
/// let spliced = bytes.splice(1, 2, replacement);
760+
///
761+
/// // `spliced` has the element [7u8]
762+
/// assert(spliced.len() == 1);
763+
/// assert(spliced.get(0).unwrap() == 7u8);
764+
///
765+
/// // `bytes` is now [5u8, 42u8, 100u8, 9u8]
766+
/// assert(bytes.len() == 4);
767+
/// assert(bytes.get(0).unwrap() == 5u8);
768+
/// assert(bytes.get(1).unwrap() == 42u8);
769+
/// assert(bytes.get(2).unwrap() == 100u8);
770+
/// assert(bytes.get(3).unwrap() == 9u8);
771+
/// }
772+
/// ```
773+
pub fn splice(ref mut self, start: u64, end: u64, replace_with: Bytes) -> Bytes {
774+
assert(start <= end);
775+
assert(end <= self.len);
776+
777+
let splice_len = end - start;
778+
let replace_len = replace_with.len();
779+
780+
// Build the Bytes to return
781+
let mut spliced = Bytes::with_capacity(splice_len);
782+
if splice_len > 0 {
783+
let old_ptr = self.buf.ptr().add_uint_offset(start);
784+
old_ptr.copy_bytes_to(spliced.buf.ptr(), splice_len);
785+
spliced.len = splice_len;
786+
}
787+
788+
// New self
789+
let new_len = self.len - splice_len + replace_len;
790+
let mut new_buf = Bytes::with_capacity(new_len);
791+
792+
// Move head
793+
if start > 0 {
794+
let old_ptr = self.buf.ptr();
795+
old_ptr.copy_bytes_to(new_buf.buf.ptr(), start);
796+
}
797+
798+
// Move middle
799+
if replace_len > 0 {
800+
replace_with
801+
.buf
802+
.ptr()
803+
.copy_bytes_to(new_buf.buf.ptr().add_uint_offset(start), replace_len);
804+
}
805+
806+
// Move tail
807+
let tail_len = self.len - end;
808+
if tail_len > 0 {
809+
let old_tail = self.buf.ptr().add_uint_offset(end);
810+
let new_tail = new_buf.buf.ptr().add_uint_offset(start + replace_len);
811+
old_tail.copy_bytes_to(new_tail, tail_len);
812+
}
813+
814+
self.buf = new_buf.buf;
815+
self.len = new_len;
816+
817+
spliced
818+
}
722819
}
723820

724821
impl core::ops::Eq for Bytes {

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

+4-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
library;
22

3-
use std::bytes::Bytes;
3+
pub mod utils;
4+
pub mod splice;
45

5-
fn setup() -> (Bytes, u8, u8, u8) {
6-
let mut bytes = Bytes::new();
7-
let a = 5u8;
8-
let b = 7u8;
9-
let c = 9u8;
10-
bytes.push(a);
11-
bytes.push(b);
12-
bytes.push(c);
13-
(bytes, a, b, c)
14-
}
6+
use utils::setup;
7+
use std::bytes::Bytes;
158

169
#[test()]
1710
fn bytes_new() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
library;
2+
3+
use std::bytes::Bytes;
4+
use ::utils::setup;
5+
6+
#[test()]
7+
fn bytes_splice() {
8+
let (mut bytes, a, b, c) = setup();
9+
bytes.push(11u8);
10+
bytes.push(13u8);
11+
// bytes = [5, 7, 9, 11, 13]
12+
13+
// Remove [1..4), replace with nothing
14+
let spliced = bytes.splice(1, 4, Bytes::new());
15+
16+
// bytes => [5, 13]
17+
assert(bytes.len() == 2);
18+
assert(bytes.get(0).unwrap() == a);
19+
assert(bytes.get(1).unwrap() == 13u8);
20+
21+
// spliced => [7, 9, 11]
22+
assert(spliced.len() == 3);
23+
assert(spliced.get(0).unwrap() == b);
24+
assert(spliced.get(1).unwrap() == c);
25+
assert(spliced.get(2).unwrap() == 11u8);
26+
}
27+
28+
#[test()]
29+
fn bytes_splice_front() {
30+
let (mut bytes, a, b, c) = setup();
31+
// Remove [0..2) => [5, 7], replace with nothing
32+
let spliced = bytes.splice(0, 2, Bytes::new());
33+
34+
// bytes => [9]
35+
assert(bytes.len() == 1);
36+
assert(bytes.get(0).unwrap() == c);
37+
38+
// spliced => [5, 7]
39+
assert(spliced.len() == 2);
40+
assert(spliced.get(0).unwrap() == a);
41+
assert(spliced.get(1).unwrap() == b);
42+
}
43+
44+
#[test()]
45+
fn bytes_splice_end() {
46+
let (mut bytes, a, b, c) = setup();
47+
// Remove [1..3) => [7, 9], replace with nothing
48+
let spliced = bytes.splice(1, bytes.len(), Bytes::new());
49+
50+
// bytes => [5]
51+
assert(bytes.len() == 1);
52+
assert(bytes.get(0).unwrap() == a);
53+
54+
// spliced => [7, 9]
55+
assert(spliced.len() == 2);
56+
assert(spliced.get(0).unwrap() == b);
57+
assert(spliced.get(1).unwrap() == c);
58+
}
59+
60+
#[test()]
61+
fn bytes_splice_empty_range() {
62+
let (mut bytes, a, b, c) = setup();
63+
// Remove [1..1) => nothing, replace with nothing
64+
let spliced = bytes.splice(1, 1, Bytes::new());
65+
66+
assert(bytes.len() == 3);
67+
assert(bytes.get(0).unwrap() == a);
68+
assert(bytes.get(1).unwrap() == b);
69+
assert(bytes.get(2).unwrap() == c);
70+
assert(spliced.len() == 0);
71+
}
72+
73+
#[test()]
74+
fn bytes_splice_entire_range() {
75+
let (mut bytes, a, b, c) = setup();
76+
// Remove [0..3) => [5, 7, 9], replace with nothing
77+
let spliced = bytes.splice(0, bytes.len(), Bytes::new());
78+
79+
assert(bytes.len() == 0);
80+
assert(bytes.is_empty());
81+
assert(spliced.len() == 3);
82+
assert(spliced.get(0).unwrap() == a);
83+
assert(spliced.get(1).unwrap() == b);
84+
assert(spliced.get(2).unwrap() == c);
85+
}
86+
87+
#[test(should_revert)]
88+
fn revert_bytes_splice_start_greater_than_end() {
89+
let (mut bytes, _, _, _) = setup();
90+
let _spliced = bytes.splice(2, 1, Bytes::new());
91+
}
92+
93+
#[test(should_revert)]
94+
fn revert_bytes_splice_end_out_of_bounds() {
95+
let (mut bytes, _, _, _) = setup();
96+
let _spliced = bytes.splice(0, bytes.len() + 1, Bytes::new());
97+
}
98+
99+
/// Additional tests for replacing a spliced range with different Byte lengths.
100+
#[test()]
101+
fn bytes_splice_replace_smaller() {
102+
let (mut bytes, a, b, c) = setup();
103+
bytes.push(11u8);
104+
bytes.push(13u8);
105+
// bytes = [5, 7, 9, 11, 13]
106+
let mut replacement = Bytes::new();
107+
replacement.push(42u8);
108+
// Remove [1..4) => [7, 9, 11], replace with [42]
109+
let spliced = bytes.splice(1, 4, replacement);
110+
111+
// bytes => [5, 42, 13]
112+
assert(bytes.len() == 3);
113+
assert(bytes.get(0).unwrap() == a);
114+
assert(bytes.get(1).unwrap() == 42u8);
115+
assert(bytes.get(2).unwrap() == 13u8);
116+
117+
// spliced => [7, 9, 11]
118+
assert(spliced.len() == 3);
119+
assert(spliced.get(0).unwrap() == b);
120+
assert(spliced.get(1).unwrap() == c);
121+
assert(spliced.get(2).unwrap() == 11u8);
122+
}
123+
124+
#[test()]
125+
fn bytes_splice_replace_larger() {
126+
let (mut bytes, a, b, c) = setup();
127+
// bytes = [5, 7, 9]
128+
let mut replacement = Bytes::new();
129+
replacement.push(42u8);
130+
replacement.push(50u8);
131+
replacement.push(60u8);
132+
// Remove [1..2) => [7], replace with [42, 50, 60]
133+
let spliced = bytes.splice(1, 2, replacement);
134+
135+
// bytes => [5, 42, 50, 60, 9]
136+
assert(bytes.len() == 5);
137+
assert(bytes.get(0).unwrap() == a);
138+
assert(bytes.get(1).unwrap() == 42u8);
139+
assert(bytes.get(2).unwrap() == 50u8);
140+
assert(bytes.get(3).unwrap() == 60u8);
141+
assert(bytes.get(4).unwrap() == c);
142+
143+
// spliced => [7]
144+
assert(spliced.len() == 1);
145+
assert(spliced.get(0).unwrap() == b);
146+
}
147+
148+
#[test()]
149+
fn bytes_splice_replace_same_length() {
150+
let (mut bytes, a, b, c) = setup();
151+
// bytes = [5, 7, 9]
152+
let mut replacement = Bytes::new();
153+
replacement.push(42u8);
154+
replacement.push(50u8);
155+
// Remove [1..3) => [7, 9], replace with [42, 50] (same length = 2)
156+
let spliced = bytes.splice(1, 3, replacement);
157+
158+
// bytes => [5, 42, 50]
159+
assert(bytes.len() == 3);
160+
assert(bytes.get(0).unwrap() == a);
161+
assert(bytes.get(1).unwrap() == 42u8);
162+
assert(bytes.get(2).unwrap() == 50u8);
163+
164+
// spliced => [7, 9]
165+
assert(spliced.len() == 2);
166+
assert(spliced.get(0).unwrap() == b);
167+
assert(spliced.get(1).unwrap() == c);
168+
}
169+
170+
#[test()]
171+
fn bytes_splice_replace_empty_bytes() {
172+
let (mut bytes, a, b, c) = setup();
173+
// bytes = [5, 7, 9]
174+
let replacement = Bytes::new();
175+
// Remove [0..1) => [5], replace with []
176+
let spliced = bytes.splice(0, 1, replacement);
177+
178+
// bytes => [7, 9]
179+
assert(bytes.len() == 2);
180+
assert(bytes.get(0).unwrap() == b);
181+
assert(bytes.get(1).unwrap() == c);
182+
183+
// spliced => [5]
184+
assert(spliced.len() == 1);
185+
assert(spliced.get(0).unwrap() == a);
186+
}
187+
188+
#[test()]
189+
fn bytes_splice_replace_overlap() {
190+
let (mut bytes, a, b, c) = setup();
191+
// bytes = [5, 7, 9]
192+
let mut replacement = Bytes::new();
193+
replacement.push(10u8);
194+
replacement.push(12u8);
195+
// Remove [0..1) => [5], replace with [10, 12]
196+
let spliced = bytes.splice(0, 1, replacement);
197+
198+
// Expect spliced to contain the removed element [5]
199+
assert(spliced.len() == 1);
200+
assert(spliced.get(0).unwrap() == a);
201+
202+
// After replacement, bytes should now be [10, 12, 7, 9]
203+
assert(bytes.len() == 4);
204+
assert(bytes.get(0).unwrap() == 10u8);
205+
assert(bytes.get(1).unwrap() == 12u8);
206+
assert(bytes.get(2).unwrap() == b);
207+
assert(bytes.get(3).unwrap() == c);
208+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
library;
2+
3+
use std::bytes::Bytes;
4+
5+
pub fn setup() -> (Bytes, u8, u8, u8) {
6+
let mut bytes = Bytes::new();
7+
let a = 5u8;
8+
let b = 7u8;
9+
let c = 9u8;
10+
bytes.push(a);
11+
bytes.push(b);
12+
bytes.push(c);
13+
(bytes, a, b, c)
14+
}

0 commit comments

Comments
 (0)