Skip to content

Commit 3ce414c

Browse files
committed
feat(sysex): splicing payload data
1 parent 6e224a9 commit 3ce414c

File tree

7 files changed

+359
-71
lines changed

7 files changed

+359
-71
lines changed

fuzz/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ doc = false
3838
bench = false
3939

4040
[[bin]]
41-
name = "generic_sysex_inserting_payloads"
42-
path = "./fuzz_targets/generic_sysex_inserting_payloads.rs"
41+
name = "generic_sysex_splicing_payloads"
42+
path = "./fuzz_targets/generic_sysex_splicing_payloads.rs"
4343
test = false
4444
doc = false
4545
bench = false

fuzz/fuzz_targets/generic_sysex_inserting_payloads.rs renamed to fuzz/fuzz_targets/generic_sysex_splicing_payloads.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ impl IntoByte<u8> for u8 {
5959
}
6060
}
6161

62-
fn test_case<B, M>(data: &InputData, mut message: M, index: usize)
62+
fn test_case<B, M, R>(data: &InputData, mut message: M, range: R)
6363
where
6464
B: midi2::buffer::Buffer + midi2::buffer::BufferTryResize + midi2::buffer::BufferMut,
6565
M: midi2::Sysex<B>,
6666
<M as Sysex<B>>::Byte: Eq + core::fmt::Debug,
6767
u8: IntoByte<<M as Sysex<B>>::Byte>,
68+
R: core::ops::RangeBounds<usize> + Clone,
6869
{
6970
let Ok(()) = message.try_set_payload(data.initial_data.iter().map(u8::byte)) else {
7071
return;
@@ -78,14 +79,16 @@ where
7879
);
7980
}
8081

81-
let Ok(()) = message.try_insert_payload(data.data_to_insert.iter().map(u8::byte), index) else {
82+
let Ok(()) =
83+
message.try_splice_payload(data.data_to_insert.iter().map(u8::byte), range.clone())
84+
else {
8285
return;
8386
};
8487

8588
let actual = message.payload().collect::<Vec<_>>();
8689
let expected = {
8790
let mut ret = data.initial_data.clone();
88-
ret.splice(index..index, data.data_to_insert.clone());
91+
ret.splice(range, data.data_to_insert.clone());
8992
ret.iter().map(u8::byte).collect::<Vec<_>>()
9093
};
9194
assert_eq!(actual, expected);
@@ -94,33 +97,41 @@ where
9497
fuzz_target!(|data: InputData| {
9598
let mut rng = rand::rngs::StdRng::seed_from_u64(data.seed);
9699
let fized_size_buffer_size = rng.random_range(4..MAX_BUFFER_SIZE);
97-
let index = if data.initial_data.is_empty() {
98-
0
99-
} else {
100-
rng.random_range(0..data.initial_data.len())
100+
let range = {
101+
if data.initial_data.is_empty() {
102+
0..0
103+
} else {
104+
let lower = rng.random_range(0..data.initial_data.len());
105+
if lower == data.initial_data.len() {
106+
lower..lower
107+
} else {
108+
let upper = rng.random_range(lower..data.initial_data.len());
109+
lower..upper
110+
}
111+
}
101112
};
102113
test_case(
103114
&data,
104115
midi2::sysex8::Sysex8::<FixedSizeBuffer<u32>>::try_new_with_buffer(
105116
FixedSizeBuffer::<u32>::new(fized_size_buffer_size),
106117
)
107118
.unwrap(),
108-
index,
119+
range.clone(),
109120
);
110121
test_case(
111122
&data,
112123
midi2::sysex7::Sysex7::<FixedSizeBuffer<u32>>::try_new_with_buffer(
113124
FixedSizeBuffer::<u32>::new(fized_size_buffer_size),
114125
)
115126
.unwrap(),
116-
index,
127+
range.clone(),
117128
);
118129
test_case(
119130
&data,
120131
midi2::sysex7::Sysex7::<FixedSizeBuffer<u8>>::try_new_with_buffer(
121132
FixedSizeBuffer::<u8>::new(fized_size_buffer_size),
122133
)
123134
.unwrap(),
124-
index,
135+
range.clone(),
125136
);
126137
});

midi2/src/detail/helpers.rs

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,17 @@ pub fn validate_sysex_group_statuses<
8989
Ok(())
9090
}
9191

92-
pub fn try_insert_sysex_data<
92+
pub fn try_splice_sysex_data<
9393
B: crate::buffer::Buffer + crate::buffer::BufferMut + crate::buffer::BufferTryResize,
9494
S: SysexInternal<B>,
9595
D: core::iter::Iterator<Item = <S as crate::traits::Sysex<B>>::Byte>,
96+
R: core::ops::RangeBounds<usize>,
9697
>(
9798
sysex: &mut S,
9899
data: D,
99-
before: usize,
100+
range: R,
100101
) -> core::result::Result<(), crate::error::BufferOverflow> {
101-
match detail::try_insert_sysex_data(sysex, data, |s, sz| s.try_resize(sz), before) {
102+
match detail::try_splice_sysex_data(sysex, data, |s, sz| s.try_resize(sz), range) {
102103
Err(e) => {
103104
// if the write failed we reset the message
104105
// back to zero data
@@ -111,23 +112,24 @@ pub fn try_insert_sysex_data<
111112
}
112113
}
113114

114-
pub fn insert_sysex_data<
115+
pub fn splice_sysex_data<
115116
B: crate::buffer::Buffer + crate::buffer::BufferMut + crate::buffer::BufferResize,
116117
S: SysexInternal<B>,
117118
D: core::iter::Iterator<Item = <S as crate::traits::Sysex<B>>::Byte>,
119+
R: core::ops::RangeBounds<usize>,
118120
>(
119121
sysex: &mut S,
120122
data: D,
121-
before: usize,
123+
range: R,
122124
) {
123-
detail::try_insert_sysex_data(
125+
detail::try_splice_sysex_data(
124126
sysex,
125127
data,
126128
|s, sz| {
127129
s.resize(sz);
128130
Ok(())
129131
},
130-
before,
132+
range,
131133
)
132134
.expect("Resizable buffers should not fail here")
133135
}
@@ -137,23 +139,37 @@ mod detail {
137139

138140
use super::*;
139141

140-
pub fn try_insert_sysex_data<
142+
pub fn try_splice_sysex_data<
141143
B: crate::buffer::Buffer + crate::buffer::BufferMut,
142144
S: crate::traits::SysexInternal<B>,
143145
D: core::iter::Iterator<Item = <S as crate::traits::Sysex<B>>::Byte>,
144146
R: Fn(&mut S, usize) -> core::result::Result<(), SysexTryResizeError>,
147+
Rg: core::ops::RangeBounds<usize>,
145148
>(
146149
sysex: &mut S,
147150
data: D,
148151
resize: R,
149-
before: usize,
152+
range: Rg,
150153
) -> core::result::Result<(), crate::error::BufferOverflow> {
151154
// reformat first to ensure data is optimally filling the
152155
// underlying buffer
153156
sysex.compact();
154157

155158
// get an initial estimate for the size of the data
156159
let initial_size = sysex.payload_size();
160+
161+
let splice_begin = match range.start_bound() {
162+
core::ops::Bound::Included(&v) => v,
163+
core::ops::Bound::Excluded(&v) => v + 1,
164+
core::ops::Bound::Unbounded => 0,
165+
};
166+
let splice_end = match range.end_bound() {
167+
core::ops::Bound::Included(&v) => v + 1,
168+
core::ops::Bound::Excluded(&v) => v,
169+
core::ops::Bound::Unbounded => initial_size,
170+
};
171+
let splice_size = splice_end - splice_begin;
172+
157173
let mut running_data_size_estimate = match data.size_hint() {
158174
(_, Some(upper)) => upper,
159175
// not the optimal case - could lead to additional copying
@@ -163,27 +179,26 @@ mod detail {
163179
let mut additional_size_for_overflow = 1;
164180
let mut data = data.peekable();
165181

166-
// initial buffer resize
167-
if let Err(SysexTryResizeError(sz)) =
168-
resize(sysex, running_data_size_estimate + initial_size)
169-
{
170-
// failed. we'll work with what we've got
171-
running_data_size_estimate = sz.saturating_sub(initial_size);
172-
};
173-
174-
debug_assert_eq!(
175-
sysex.payload_size(),
176-
running_data_size_estimate + initial_size
177-
);
182+
if splice_end < splice_end + running_data_size_estimate - splice_size {
183+
// we need to grow
184+
// initial buffer resize
185+
if let Err(SysexTryResizeError(sz)) = resize(
186+
sysex,
187+
running_data_size_estimate + initial_size - splice_size,
188+
) {
189+
// failed. we'll work with what we've got
190+
running_data_size_estimate = sz.saturating_sub(initial_size - splice_size);
191+
};
192+
}
178193

179-
let mut tail = before + running_data_size_estimate;
180-
sysex.move_payload_tail(before, tail);
194+
let mut tail = splice_end + running_data_size_estimate - splice_size;
195+
sysex.move_payload_tail(splice_end, tail);
181196

182197
'main: loop {
183198
while written < running_data_size_estimate {
184199
match data.next() {
185200
Some(v) => {
186-
sysex.write_datum(v, before + written);
201+
sysex.write_datum(v, splice_begin + written);
187202
written += 1;
188203
}
189204
None => {
@@ -199,28 +214,41 @@ mod detail {
199214
}
200215

201216
// we underestimated.
202-
// resize to make more space
203217
running_data_size_estimate += additional_size_for_overflow;
204-
if let Err(SysexTryResizeError(sz)) =
205-
resize(sysex, running_data_size_estimate + initial_size)
218+
206219
{
207-
// failed. we'll work with what we've got
208-
running_data_size_estimate = sz.saturating_sub(initial_size);
209-
};
210-
sysex.move_payload_tail(tail, before + running_data_size_estimate);
211-
tail = before + running_data_size_estimate;
220+
let mut to = splice_begin + running_data_size_estimate;
221+
if tail < to {
222+
// we need to grow
223+
// resize to make more space
224+
// and move tail
225+
226+
if let Err(SysexTryResizeError(sz)) = resize(
227+
sysex,
228+
running_data_size_estimate + initial_size - splice_size,
229+
) {
230+
// failed. we'll work with what we've got
231+
running_data_size_estimate = sz.saturating_sub(initial_size - splice_size);
232+
to = splice_begin + running_data_size_estimate - splice_size;
233+
};
234+
}
235+
236+
sysex.move_payload_tail(tail, to);
237+
tail = splice_begin + running_data_size_estimate;
238+
}
239+
212240
additional_size_for_overflow *= 2;
213241

214242
if written >= running_data_size_estimate {
215243
return Err(BufferOverflow);
216244
}
217245
}
218246

219-
if written < running_data_size_estimate {
220-
// we shrink the buffer back down to the correct size
221-
sysex.move_payload_tail(tail, before + written);
222-
resize(sysex, written + initial_size).map_err(|_| crate::error::BufferOverflow)?;
223-
}
247+
// we ensure the buffer is the correct size and move the tail
248+
// to the final position
249+
sysex.move_payload_tail(tail, splice_begin + written);
250+
resize(sysex, written + initial_size - splice_size)
251+
.map_err(|_| crate::error::BufferOverflow)?;
224252

225253
Ok(())
226254
}
Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
11
/// This is for testing payload insertion implementation on sysex messages.
22
/// The iterator returns no size hints so the optimisation case for these
33
/// payload insertion implementations will hit their worst case for mem-copying.
4-
pub struct RubbishPayloadIterator(u8);
4+
pub struct RubbishPayloadIterator<I: core::iter::Iterator<Item = u8>>(I);
55

6-
impl RubbishPayloadIterator {
6+
const DEFAULT_DATA: [u8; 50] = [
7+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
8+
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
9+
];
10+
11+
impl RubbishPayloadIterator<core::iter::Cloned<core::slice::Iter<'static, u8>>> {
712
pub fn new() -> Self {
8-
RubbishPayloadIterator(0)
13+
RubbishPayloadIterator(DEFAULT_DATA.iter().cloned())
14+
}
15+
}
16+
17+
impl<I: core::iter::Iterator<Item = u8>> core::convert::From<I> for RubbishPayloadIterator<I> {
18+
fn from(iter: I) -> Self {
19+
RubbishPayloadIterator(iter)
920
}
1021
}
1122

12-
impl core::iter::Iterator for RubbishPayloadIterator {
23+
impl<I: core::iter::Iterator<Item = u8>> core::iter::Iterator for RubbishPayloadIterator<I> {
1324
type Item = u8;
1425
fn next(&mut self) -> Option<Self::Item> {
15-
if self.0 == 50 {
16-
return None;
17-
}
18-
let ret = Some(self.0);
19-
self.0 += 1;
20-
ret
26+
self.0.next()
27+
}
28+
}
29+
30+
mod tests {
31+
use super::*;
32+
33+
#[test]
34+
fn rubbish_iterator_should_give_worst_case_bounds() {
35+
assert_eq!(RubbishPayloadIterator::new().size_hint(), (0, None));
2136
}
2237
}

0 commit comments

Comments
 (0)