Skip to main content

midi_io/midi/
sys_ex.rs

1use crate::SysExError;
2
3/// Max Sysex size is limited to prevent denial-of-service through unlimited allocation.
4pub(crate) const MAX_SYSEX_BYTES: usize = 1024 * 1024;
5
6/// The amount of orphaned data bytes show as part of an error. Limited to prevent denial-of-service through unlimited allocation.
7pub(crate) const ORPHAN_PREFIX_BYTES: usize = 64;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub struct SysEx(Vec<u8>);
11
12impl TryFrom<&[u8]> for SysEx {
13    type Error = SysExError;
14
15    fn try_from(wire: &[u8]) -> Result<Self, Self::Error> {
16        if wire.first() != Some(&0xF0) {
17            return Err(SysExError::MissingStart);
18        }
19        if wire.len() < 2 || wire.last() != Some(&0xF7) {
20            return Err(SysExError::Unterminated);
21        }
22        Self::new(&wire[1..wire.len() - 1])
23    }
24}
25
26impl SysEx {
27    pub fn new(body: &[u8]) -> Result<Self, SysExError> {
28        if body.is_empty() {
29            return Err(SysExError::EmptyBody);
30        }
31        if body.len() > MAX_SYSEX_BYTES {
32            return Err(SysExError::TooLong {
33                len: body.len(),
34                max: MAX_SYSEX_BYTES,
35            });
36        }
37        if let Some(index) = body.iter().position(|&b| b > 0x7F) {
38            return Err(SysExError::HighBit {
39                index,
40                byte: body[index],
41            });
42        }
43        Ok(Self(body.to_vec()))
44    }
45
46    pub fn bytes(&self) -> &[u8] {
47        &self.0
48    }
49
50    pub fn to_wire_bytes(&self) -> Vec<u8> {
51        let mut out = Vec::with_capacity(self.0.len() + 2);
52        out.push(0xF0);
53        out.extend_from_slice(&self.0);
54        out.push(0xF7);
55        out
56    }
57}
58
59impl std::ops::Deref for SysEx {
60    type Target = [u8];
61
62    fn deref(&self) -> &Self::Target {
63        &self.0
64    }
65}
66
67impl AsRef<[u8]> for SysEx {
68    fn as_ref(&self) -> &[u8] {
69        &self.0
70    }
71}
72
73impl std::fmt::Display for SysEx {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "SysEx({} bytes)", self.0.len())
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn sysex_new_builds_from_unframed_body() {
85        let sysex = SysEx::new(&[0x41, 0x10, 0x20]).unwrap();
86        assert_eq!(sysex.bytes(), &[0x41, 0x10, 0x20]);
87        assert_eq!(sysex.to_wire_bytes(), vec![0xF0, 0x41, 0x10, 0x20, 0xF7]);
88    }
89
90    #[test]
91    fn sysex_new_rejects_empty_body() {
92        assert_eq!(SysEx::new(&[]), Err(SysExError::EmptyBody));
93    }
94
95    #[test]
96    fn sysex_new_rejects_high_bit_body() {
97        assert_eq!(
98            SysEx::new(&[0x41, 0x80, 0x10]),
99            Err(SysExError::HighBit {
100                index: 1,
101                byte: 0x80
102            })
103        );
104    }
105
106    #[test]
107    fn sysex_new_rejects_oversized_body() {
108        let body = vec![0x01u8; MAX_SYSEX_BYTES + 1];
109        assert_eq!(
110            SysEx::new(&body),
111            Err(SysExError::TooLong {
112                len: MAX_SYSEX_BYTES + 1,
113                max: MAX_SYSEX_BYTES
114            })
115        );
116    }
117
118    #[test]
119    fn sysex_try_from_parses_wire_frame() {
120        assert_eq!(
121            SysEx::try_from([0xF0u8, 0x41, 0x10, 0xF7].as_slice())
122                .unwrap()
123                .bytes(),
124            &[0x41, 0x10]
125        );
126    }
127
128    #[test]
129    fn sysex_try_from_requires_start_byte() {
130        assert_eq!(
131            SysEx::try_from([0x41u8, 0x10, 0xF7].as_slice()),
132            Err(SysExError::MissingStart)
133        );
134    }
135
136    #[test]
137    fn sysex_try_from_requires_end_byte() {
138        assert_eq!(
139            SysEx::try_from([0xF0u8, 0x41, 0x10].as_slice()),
140            Err(SysExError::Unterminated)
141        );
142    }
143
144    #[test]
145    fn sysex_try_from_requires_nonempty_body() {
146        assert_eq!(
147            SysEx::try_from([0xF0u8, 0xF7].as_slice()),
148            Err(SysExError::EmptyBody)
149        );
150    }
151
152    #[test]
153    fn sysex_try_from_rejects_high_bit_body() {
154        assert_eq!(
155            SysEx::try_from([0xF0u8, 0x41, 0x80, 0xF7].as_slice()),
156            Err(SysExError::HighBit {
157                index: 1,
158                byte: 0x80
159            })
160        );
161    }
162
163    #[test]
164    fn sysex_try_from_rejects_oversized_body() {
165        let mut wire = vec![0xF0u8];
166        wire.extend(std::iter::repeat(0x01).take(MAX_SYSEX_BYTES + 1));
167        wire.push(0xF7);
168        assert_eq!(
169            SysEx::try_from(wire.as_slice()),
170            Err(SysExError::TooLong {
171                len: MAX_SYSEX_BYTES + 1,
172                max: MAX_SYSEX_BYTES
173            })
174        );
175    }
176}