Skip to content

Commit d92221d

Browse files
committed
chore: Merge branch 'release/0.11.0'
2 parents ded7669 + 35f13d9 commit d92221d

25 files changed

+524
-167
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repos:
1010
- id: conventional-pre-commit
1111
stages: [commit-msg]
1212
- repo: https://github.com/pre-commit/pre-commit-hooks
13-
rev: v5.0.0
13+
rev: v6.0.0
1414
hooks:
1515
- id: check-added-large-files
1616
stages: [pre-commit]

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## [0.11.0](https://github.com/midi2-dev/bl-midi2-rs/compare/0.10.0..0.11.0) - 2025-08-21
4+
5+
### ✨ Features
6+
7+
- *(sysex)* Splicing payload data - ([3ce414c](https://github.com/midi2-dev/bl-midi2-rs/commit/3ce414c4576f82497518f5a8c7396126b628ea6d))
8+
9+
### 🐛 Fixes
10+
11+
- Include trailing reserved bytes - ([6e224a9](https://github.com/midi2-dev/bl-midi2-rs/commit/6e224a92c9aab5595aa12a373b2996a831f33bdd))
12+
- Further clippy warnings - ([8bbee8b](https://github.com/midi2-dev/bl-midi2-rs/commit/8bbee8ba8932b13aa018befd98e88a6f8e0c68d1))
13+
- Readme file link - ([eaa0c41](https://github.com/midi2-dev/bl-midi2-rs/commit/eaa0c41e24c8e1f70a8a1168d93705b14e722907))
14+
315
## [0.10.0](https://github.com/midi2-dev/bl-midi2-rs/compare/0.9.0..0.10.0) - 2025-07-27
416

517
### 🐛 Fixes

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ for more details on the data protocol standard.
1818
> Expect breaking changes and bugs, and please report any issues you encounter.
1919
2020
We would welcome contributions!
21-
Please refer to the [CONTRIBUTOR.md](CONTRIBUTOR.md)
21+
Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md)
2222

2323
## Strongly Typed Message Wrappers
2424

examples/handling_messages/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn handle_message(buffer: &[u32]) {
2929
);
3030
}
3131
Err(e) => {
32-
println!("Error parsing ump buffer: {:?}", e);
32+
println!("Error parsing ump buffer: {e:?}");
3333
}
3434
_ => {}
3535
}

examples/no_std_dynamic_message_generator/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl Generator {
6767
}
6868
}
6969

70-
fn generate(&mut self) -> Result<MessageIterator, midi2::error::BufferOverflow> {
70+
fn generate(&mut self) -> Result<MessageIterator<'_>, midi2::error::BufferOverflow> {
7171
let mut number_of_messages = 0;
7272
let buffer = &mut self.buffer[..];
7373

@@ -113,6 +113,6 @@ impl Generator {
113113
fn main() {
114114
let mut generator = Generator::new();
115115
for message in generator.generate().unwrap() {
116-
std::println!("{:?}", message);
116+
std::println!("{message:?}");
117117
}
118118
}

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/channel_voice2/per_note_management.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub(crate) const STATUS: u8 = 0b1111;
99
/// MIDI 2.0 Channel Voice Per Note Management Message
1010
///
1111
/// See the [module docs](crate::channel_voice2) for more info.
12-
#[midi2_proc::generate_message(Via(crate::channel_voice2::ChannelVoice2), FixedSize, MinSizeUmp(1))]
12+
#[midi2_proc::generate_message(Via(crate::channel_voice2::ChannelVoice2), FixedSize, MinSizeUmp(2))]
1313
struct PerNoteManagement {
1414
#[property(common_properties::UmpMessageTypeProperty<UMP_MESSAGE_TYPE>)]
1515
ump_type: (),
@@ -49,7 +49,7 @@ mod tests {
4949
#[test]
5050
fn note_number() {
5151
assert_eq!(
52-
PerNoteManagement::try_from(&[0x4BF9_1C03][..])
52+
PerNoteManagement::try_from(&[0x4BF9_1C03, 0x0][..])
5353
.unwrap()
5454
.note_number(),
5555
u7::new(0x1C),
@@ -58,14 +58,14 @@ mod tests {
5858

5959
#[test]
6060
fn detach() {
61-
assert!(PerNoteManagement::try_from(&[0x4BF9_1C03][..])
61+
assert!(PerNoteManagement::try_from(&[0x4BF9_1C03, 0x0][..])
6262
.unwrap()
6363
.detach(),);
6464
}
6565

6666
#[test]
6767
fn reset() {
68-
assert!(PerNoteManagement::try_from(&[0x4BF9_1C03][..])
68+
assert!(PerNoteManagement::try_from(&[0x4BF9_1C03, 0x0][..])
6969
.unwrap()
7070
.reset(),);
7171
}

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
}

0 commit comments

Comments
 (0)