wmidi/
byte.rs

1use crate::Error;
2use core::convert::TryFrom;
3
4/// A data byte that holds 7 bits of information.
5#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
6pub struct U7(pub(crate) u8);
7
8impl U7 {
9    /// The minimum value for a u7 data byte.
10    pub const MIN: U7 = U7(0x00);
11    /// The maximum value for a u7 data byte.
12    pub const MAX: U7 = U7(0x80 - 0x01);
13
14    /// Create a new `U7` or return an error if it is out of range.
15    #[inline(always)]
16    pub fn new(data: u8) -> Result<U7, Error> {
17        if data > u8::from(U7::MAX) {
18            Err(Error::DataByteOutOfRange)
19        } else {
20            Ok(U7(data))
21        }
22    }
23
24    /// Convert a `u8` into a `U7` without bounds checking.
25    ///
26    /// # Safety
27    /// Behavior is undefined if data > 127.
28    #[inline(always)]
29    pub unsafe fn from_unchecked(data: u8) -> U7 {
30        U7(data)
31    }
32
33    /// Create a `U7` from a `u8`. Only the 7 least significant bits of `note` are kept.
34    #[inline(always)]
35    pub const fn from_u8_lossy(data: u8) -> U7 {
36        U7(data & 0x7F)
37    }
38
39    /// Convert a slice of `u8` into a slice of `U7`. If any of the data is out of range, then an
40    /// error is returned.
41    #[inline(always)]
42    pub fn try_from_bytes(bytes: &[u8]) -> Result<&[U7], Error> {
43        for b in bytes.iter() {
44            U7::try_from(*b)?;
45        }
46        unsafe { Ok(U7::from_bytes_unchecked(bytes)) }
47    }
48
49    /// Convert a slice of `U7` into a slice `u8`. Since `U7` is a subset of `u8`, this is a simple
50    /// cast.
51    #[inline(always)]
52    pub fn data_to_bytes(data: &[U7]) -> &[u8] {
53        unsafe { &*(data as *const [U7] as *const [u8]) }
54    }
55
56    /// Convert a slice of `u8` to a slice of `U7` without bounds checking.
57    ///
58    /// # Safety
59    /// Behavior is undefined if any byte is > 127.
60    #[inline(always)]
61    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &[U7] {
62        &*(bytes as *const [u8] as *const [U7])
63    }
64}
65
66impl From<U7> for u8 {
67    #[inline(always)]
68    fn from(data: U7) -> u8 {
69        data.0
70    }
71}
72
73impl TryFrom<u8> for U7 {
74    type Error = Error;
75
76    #[inline(always)]
77    fn try_from(data: u8) -> Result<U7, Error> {
78        U7::new(data)
79    }
80}
81
82/// A combination of 2 data bytes that holds 14 bits of information.
83#[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
84pub struct U14(u16);
85
86impl U14 {
87    /// The minimum value for a u14 data byte.
88    pub const MIN: U14 = U14(0);
89    /// The maximum value for a u7 data byte.
90    pub const MAX: U14 = U14(0x4000 - 0x0001);
91
92    /// Convert a `u8` into a `U7` without bounds checking.
93    ///
94    /// # Safety
95    /// Behavior is undefined if data is > 16383.
96    #[inline(always)]
97    pub unsafe fn from_unchecked(data: u16) -> U14 {
98        U14(data)
99    }
100
101    /// Convert a slice of `u16` into a slice of `U14`. If any of the data is out of range, then an
102    /// error is returned.
103    #[inline(always)]
104    pub fn try_from_slice(slice: &[u16]) -> Result<&[U14], Error> {
105        for d in slice.iter() {
106            U14::try_from(*d)?;
107        }
108        unsafe { Ok(U14::from_slice_unchecked(slice)) }
109    }
110
111    /// Convert a slice of `U14` into a slice `u16`. Since `U14` is a subset of `u16`, this is a
112    /// simple cast.
113    #[inline(always)]
114    pub fn data_to_slice(data: &[U14]) -> &[u16] {
115        unsafe { &*(data as *const [U14] as *const [u16]) }
116    }
117
118    /// Convert a slice of `u16` to a slice of `U14` without bounds checking.
119    ///
120    /// # Safety
121    /// Behavior is undefined if any byte is > 16383.
122    #[inline(always)]
123    pub unsafe fn from_slice_unchecked(slice: &[u16]) -> &[U14] {
124        &*(slice as *const [u16] as *const [U14])
125    }
126}
127
128impl From<U14> for u16 {
129    #[inline(always)]
130    fn from(data: U14) -> u16 {
131        data.0
132    }
133}
134
135impl TryFrom<u16> for U14 {
136    type Error = Error;
137
138    #[inline(always)]
139    fn try_from(data: u16) -> Result<U14, Error> {
140        if data > u16::from(U14::MAX) {
141            Err(Error::U14OutOfRange)
142        } else {
143            Ok(U14(data))
144        }
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn try_from_passes() {
154        for n in 0x00..0x80 {
155            U7::try_from(n).unwrap();
156        }
157    }
158
159    #[test]
160    fn min_and_max_constant_are_valid() {
161        assert_eq!(U7::try_from(u8::from(U7::MIN)).unwrap(), U7::MIN);
162        assert_eq!(U7::try_from(u8::from(U7::MAX)).unwrap(), U7::MAX);
163    }
164
165    #[test]
166    fn try_from_out_of_range_fails() {
167        for n in 0x80..=u8::MAX {
168            assert_eq!(U7::try_from(n), Err(Error::DataByteOutOfRange));
169        }
170    }
171
172    #[test]
173    fn try_from_bytes_is_ok_on_valid_bytes() {
174        U7::try_from_bytes(&[]).unwrap();
175        U7::try_from_bytes(&[0x00, 0x08, 0x10, 0x20, 0x30, 0x40, 0x7F]).unwrap();
176    }
177
178    #[test]
179    fn try_from_bytes_fails_on_out_of_range() {
180        assert_eq!(
181            U7::try_from_bytes(&[0x00, 0x80]),
182            Err(Error::DataByteOutOfRange)
183        );
184    }
185
186    #[test]
187    fn data_to_bytes_converts_exactly() {
188        assert_eq!(
189            &[0x00, 0x0F, 0x7F],
190            U7::data_to_bytes(&[
191                U7::try_from(0x00).unwrap(),
192                U7::try_from(0x0F).unwrap(),
193                U7::try_from(0x7F).unwrap()
194            ]),
195        );
196    }
197
198    #[test]
199    fn try_from_16_passes() {
200        for n in 0x0000..0x4000 {
201            U14::try_from(n).unwrap();
202        }
203    }
204
205    #[test]
206    fn min_and_max_14_constant_are_valid() {
207        assert_eq!(U14::try_from(u16::from(U14::MIN)).unwrap(), U14::MIN);
208        assert_eq!(U14::try_from(u16::from(U14::MAX)).unwrap(), U14::MAX);
209    }
210
211    #[test]
212    fn try_from_out_of_range_16_fails() {
213        for n in 0x4000..=u16::MAX {
214            assert_eq!(U14::try_from(n), Err(Error::U14OutOfRange));
215        }
216    }
217
218    #[test]
219    fn try_from_slice_is_ok_on_valid_range() {
220        U14::try_from_slice(&[]).unwrap();
221        U14::try_from_slice(&[0x0000, 0x0080, 0x0180, 0x01FF]).unwrap();
222    }
223
224    #[test]
225    fn try_from_slice_fails_on_out_of_range() {
226        assert_eq!(
227            U14::try_from_slice(&[0x0000, 0x5000]),
228            Err(Error::U14OutOfRange)
229        );
230    }
231
232    #[test]
233    fn data_to_slice_converts_exactly() {
234        assert_eq!(
235            &[0x0000, 0x010F, 0x017F],
236            U14::data_to_slice(&[
237                U14::try_from(0x0000).unwrap(),
238                U14::try_from(0x010F).unwrap(),
239                U14::try_from(0x017F).unwrap()
240            ]),
241        );
242    }
243
244    #[test]
245    fn test_from_u8_lossy() {
246        assert_eq!(U7::from_u8_lossy(0), U7::try_from(0).unwrap());
247        assert_eq!(U7::from_u8_lossy(64), U7::try_from(64).unwrap());
248        assert_eq!(U7::from_u8_lossy(127), U7::try_from(127).unwrap());
249        assert_eq!(U7::from_u8_lossy(128), U7::try_from(0).unwrap());
250        assert_eq!(U7::from_u8_lossy(200), U7::try_from(72).unwrap());
251    }
252}