1use crate::SysExError;
2
3pub(crate) const MAX_SYSEX_BYTES: usize = 1024 * 1024;
5
6pub(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}