diff --git a/midi2_proc/src/derives.rs b/midi2_proc/src/derives.rs index 5359c76..4dabcb9 100644 --- a/midi2_proc/src/derives.rs +++ b/midi2_proc/src/derives.rs @@ -375,16 +375,25 @@ pub fn debug(item: TokenStream1) -> TokenStream1 { _ => panic!("Only enums and structs supported"), }; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - let buffer_id = common::buffer_generic(generics) - .expect("Expected buffer generic") - .ident(); + + let buffer_id = if ident == "Packet" { + // special handling + // always a u32 slice + quote! {crate::buffer::UNIT_ID_U32} + } else { + let buffer_ident = common::buffer_generic(generics) + .expect("Expected buffer generic") + .ident(); + quote! {<<#buffer_ident as crate::buffer::Buffer>::Unit as crate::buffer::UnitPrivate>::UNIT_ID} + }; + quote! { impl #impl_generics core::fmt::Debug for #ident #ty_generics #where_clause { fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { use crate::BufferAccess as BufferAccessDeriveDebug; fmt.write_fmt(format_args!("{}([", stringify!(#ident)))?; - match <<#buffer_id as crate::buffer::Buffer>::Unit as crate::buffer::UnitPrivate>::UNIT_ID { + match #buffer_id { crate::buffer::UNIT_ID_U8 => { use crate::buffer::SpecialiseU8 as SpecialiseU8DeriveDebug; diff --git a/midi2_proc/src/generate_ci.rs b/midi2_proc/src/generate_ci.rs index 2230274..b7e2cea 100644 --- a/midi2_proc/src/generate_ci.rs +++ b/midi2_proc/src/generate_ci.rs @@ -542,7 +542,7 @@ fn try_from_slice_impl(root_ident: &syn::Ident, properties: &[Property]) -> Toke type Error = crate::error::InvalidData; fn try_from(buffer: &'a [u8]) -> core::result::Result { if buffer.len() < >::MIN_SIZE { - return Err(crate::error::InvalidData("Slice is too short")); + return Err(crate::error::InvalidData(crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT)); } if buffer[5] != VERSION { return Err(crate::error::InvalidData("Incorrect CI version")); diff --git a/src/channel_voice1.rs b/src/channel_voice1.rs index 1bc5254..3fbd25d 100644 --- a/src/channel_voice1.rs +++ b/src/channel_voice1.rs @@ -7,6 +7,7 @@ mod control_change; mod key_pressure; mod note_off; mod note_on; +mod packet; mod pitch_bend; mod program_change; @@ -15,6 +16,7 @@ pub use control_change::*; pub use key_pressure::*; pub use note_off::*; pub use note_on::*; +pub use packet::Packet; pub use pitch_bend::*; pub use program_change::*; @@ -52,7 +54,9 @@ impl<'a, U: crate::buffer::Unit> core::convert::TryFrom<&'a [U]> for ChannelVoic type Error = crate::error::InvalidData; fn try_from(buffer: &'a [U]) -> Result { if buffer.is_empty() { - return Err(crate::error::InvalidData("Slice is too short")); + return Err(crate::error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); }; Ok(match status(buffer) { channel_pressure::STATUS => ChannelPressure::try_from(buffer)?.into(), @@ -183,7 +187,7 @@ mod test { fn packets() { let message = ChannelVoice1::try_from(&[0x2FD6_0900_u32][..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x2FD6_0900_u32][..])); + assert_eq!(&*packets.next().unwrap(), &[0x2FD6_0900_u32][..]); assert_eq!(packets.next(), None); } diff --git a/src/channel_voice1/channel_pressure.rs b/src/channel_voice1/channel_pressure.rs index bdec04a..67861da 100644 --- a/src/channel_voice1/channel_pressure.rs +++ b/src/channel_voice1/channel_pressure.rs @@ -97,7 +97,9 @@ mod tests { fn from_empty_data() { assert_eq!( ChannelPressure::try_from(&<[u32; 0] as Default>::default()[..]), - Err(crate::error::InvalidData("Slice is too short")), + Err(crate::error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), ); } diff --git a/src/channel_voice1/control_change.rs b/src/channel_voice1/control_change.rs index 3edd7e3..d90939b 100644 --- a/src/channel_voice1/control_change.rs +++ b/src/channel_voice1/control_change.rs @@ -137,7 +137,7 @@ mod tests { let buffer = [0x2AB7_3637_u32]; let message = ControlChange::try_from(&buffer[..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x2AB7_3637_u32][..])); + assert_eq!(&*packets.next().unwrap(), &[0x2AB7_3637_u32][..]); assert_eq!(packets.next(), None); } } diff --git a/src/channel_voice1/packet.rs b/src/channel_voice1/packet.rs new file mode 100644 index 0000000..3c066dc --- /dev/null +++ b/src/channel_voice1/packet.rs @@ -0,0 +1,106 @@ +use crate::{channel_voice1, error}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub [u32; 1]); + +impl crate::traits::BufferAccess<[u32; 1]> for Packet { + fn buffer_access(&self) -> &[u32; 1] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 1] + where + [u32; 1]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.is_empty() { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != channel_voice1::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + Ok(Packet({ + let mut buffer = [0x0; 1]; + buffer[..data.len()].copy_from_slice(data); + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +impl crate::Grouped<[u32; 1]> for Packet { + fn group(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(1) + } + fn set_group(&mut self, group: crate::ux::u4) + where + [u32; 1]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(1, group); + } +} + +impl crate::Channeled<[u32; 1]> for Packet { + fn channel(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(3) + } + fn set_channel(&mut self, channel: crate::ux::u4) + where + [u32; 1]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(3, channel); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn construction() { + assert!(Packet::try_from(&[0x2000_0000][..]).is_ok()); + } + + #[test] + fn construction_incorrect_ump_message_type() { + assert_eq!( + Packet::try_from(&[0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ); + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ); + } +} diff --git a/src/channel_voice2.rs b/src/channel_voice2.rs index db6c95d..2478b05 100644 --- a/src/channel_voice2.rs +++ b/src/channel_voice2.rs @@ -10,6 +10,7 @@ mod controller; mod key_pressure; mod note_off; mod note_on; +mod packet; mod per_note_management; mod per_note_pitch_bend; mod program_change; @@ -28,6 +29,7 @@ pub use controller::Controller; pub use key_pressure::*; pub use note_off::*; pub use note_on::*; +pub use packet::Packet; pub use per_note_management::*; pub use per_note_pitch_bend::*; pub use program_change::*; @@ -74,7 +76,9 @@ impl<'a> TryFrom<&'a [u32]> for ChannelVoice2<&'a [u32]> { type Error = crate::error::InvalidData; fn try_from(buffer: &'a [u32]) -> Result { if buffer.is_empty() { - return Err(crate::error::InvalidData("Slice is too short")); + return Err(crate::error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); }; use crate::detail::BitOps; @@ -148,7 +152,7 @@ mod test { let message = ChannelVoice2::try_from(&[0x4BAC_5900, 0xC0B83064][..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x4BAC_5900, 0xC0B83064][..])); + assert_eq!(&*packets.next().unwrap(), &[0x4BAC_5900, 0xC0B83064][..]); assert_eq!(packets.next(), None); } diff --git a/src/channel_voice2/assignable_controller.rs b/src/channel_voice2/assignable_controller.rs index 539dac9..6321308 100644 --- a/src/channel_voice2/assignable_controller.rs +++ b/src/channel_voice2/assignable_controller.rs @@ -109,7 +109,7 @@ mod tests { let buffer = [0x4C38_5138, 0x3F3ADD42]; let message = AssignableController::try_from(&buffer[..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x4C38_5138, 0x3F3ADD42][..])); + assert_eq!(&*packets.next().unwrap(), &[0x4C38_5138, 0x3F3ADD42][..]); assert_eq!(packets.next(), None); } } diff --git a/src/channel_voice2/packet.rs b/src/channel_voice2/packet.rs new file mode 100644 index 0000000..01bb43e --- /dev/null +++ b/src/channel_voice2/packet.rs @@ -0,0 +1,106 @@ +use crate::{channel_voice2, error}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 2]); + +impl crate::traits::BufferAccess<[u32; 2]> for Packet { + fn buffer_access(&self) -> &[u32; 2] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 2] + where + [u32; 2]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.is_empty() { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != channel_voice2::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + Ok(Packet({ + let mut buffer = [0x0; 2]; + buffer[..data.len()].copy_from_slice(data); + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +impl crate::Grouped<[u32; 2]> for Packet { + fn group(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(1) + } + fn set_group(&mut self, group: crate::ux::u4) + where + [u32; 2]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(1, group); + } +} + +impl crate::Channeled<[u32; 2]> for Packet { + fn channel(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(3) + } + fn set_channel(&mut self, channel: crate::ux::u4) + where + [u32; 2]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(3, channel); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn construction() { + assert!(Packet::try_from(&[0x4000_0000][..]).is_ok()); + } + + #[test] + fn construction_incorrect_ump_message_type() { + assert_eq!( + Packet::try_from(&[0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ); + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ); + } +} diff --git a/src/detail.rs b/src/detail.rs index 94a5eb6..1cafda5 100644 --- a/src/detail.rs +++ b/src/detail.rs @@ -1,6 +1,7 @@ mod bit_ops; mod encode_7bit; +pub mod common_err_strings; pub mod common_properties; pub mod helpers; pub mod property; diff --git a/src/detail/common_err_strings.rs b/src/detail/common_err_strings.rs new file mode 100644 index 0000000..339c9c3 --- /dev/null +++ b/src/detail/common_err_strings.rs @@ -0,0 +1,2 @@ +pub const ERR_SLICE_TOO_SHORT: &str = "Slice is too short"; +pub const ERR_INCORRECT_UMP_MESSAGE_TYPE: &str = "Incorrect UMP message type"; diff --git a/src/error.rs b/src/error.rs index c252bab..7a8b411 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,21 @@ +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Error { + BufferOverflow, + InvalidData(InvalidData), +} + +impl core::convert::From for Error { + fn from(_value: BufferOverflow) -> Self { + Error::BufferOverflow + } +} + +impl core::convert::From for Error { + fn from(value: InvalidData) -> Self { + Error::InvalidData(value) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct BufferOverflow; diff --git a/src/flex_data.rs b/src/flex_data.rs index ae879e4..114da0c 100644 --- a/src/flex_data.rs +++ b/src/flex_data.rs @@ -8,6 +8,7 @@ use crate::{ }, }; +mod packet; mod text; mod set_chord_name; @@ -775,6 +776,7 @@ pub use lyricist_name::*; pub use lyrics::*; pub use lyrics_language::*; pub use midi_clip_name::*; +pub use packet::{Format, Packet}; pub use primary_performer_name::*; pub use project_name::*; pub use publisher_name::*; @@ -847,7 +849,9 @@ impl<'a> TryFrom<&'a [u32]> for FlexData<&'a [u32]> { fn try_from(value: &'a [u32]) -> Result { use FlexData::*; if value.is_empty() { - return Err(crate::error::InvalidData("Slice is too short")); + return Err(crate::error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); }; Ok(match value[0].word(1) { 0x00_00 => SetTempo(set_tempo::SetTempo::try_from(value)?), @@ -1239,17 +1243,7 @@ mod tests { } #[test] - fn packets_small() { - use crate::Packets; - - let message = FlexData::try_from(&[0xD710_0000_u32, 0xF751_FE05][..]).unwrap(); - let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0xD710_0000_u32, 0xF751_FE05][..])); - assert_eq!(packets.next(), None); - } - - #[test] - fn packets_big() { + fn packets() { use crate::Packets; let message = FlexData::try_from( @@ -1268,12 +1262,12 @@ mod tests { let mut packets = message.packets(); assert_eq!( - packets.next(), - Some(&[0xD050_0106, 0x4769_6D6D, 0x6520_736F, 0x6D65_2073,][..]) + &*packets.next().unwrap(), + &[0xD050_0106, 0x4769_6D6D, 0x6520_736F, 0x6D65_2073,][..], ); assert_eq!( - packets.next(), - Some(&[0xD0D0_0106, 0x6967_6E61, 0x6C21_0000, 0x0000_0000,][..]) + &*packets.next().unwrap(), + &[0xD0D0_0106, 0x6967_6E61, 0x6C21_0000, 0x0000_0000,][..], ); assert_eq!(packets.next(), None); } diff --git a/src/flex_data/packet.rs b/src/flex_data/packet.rs new file mode 100644 index 0000000..1fc3739 --- /dev/null +++ b/src/flex_data/packet.rs @@ -0,0 +1,156 @@ +use crate::{error, flex_data}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 4]); + +impl crate::traits::BufferAccess<[u32; 4]> for Packet { + fn buffer_access(&self) -> &[u32; 4] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 4] + where + [u32; 4]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.len() < 2 { + return Err(error::InvalidData("Slice is too short")); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != flex_data::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + Ok(Packet({ + let mut buffer = [0x0; 4]; + buffer[..data.len()].copy_from_slice(data); + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +impl crate::Grouped<[u32; 4]> for Packet { + fn group(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(1) + } + fn set_group(&mut self, group: crate::ux::u4) + where + [u32; 4]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(1, group); + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Format { + Complete, + Start, + Continue, + End, +} + +impl Packet { + pub fn format(&self) -> Format { + format_from_data(&self.0[..]) + } +} + +fn format_from_data(data: &[u32]) -> Format { + use crate::detail::BitOps; + use Format::*; + match u8::from(data[0].crumb(4)) { + 0x0 => Complete, + 0x1 => Start, + 0x2 => Continue, + 0x3 => End, + _ => unreachable!(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn construction() { + assert!(Packet::try_from(&[0xD000_0000, 0x0000_0000][..]).is_ok()) + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[0xD000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ) + } + + #[test] + fn construction_incorrect_ump_message_type() { + assert_eq!( + Packet::try_from(&[0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ) + } + + #[test] + fn complete_format() { + assert_eq!( + Packet::try_from(&[0xD000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::Complete, + ) + } + + #[test] + fn start_format() { + assert_eq!( + Packet::try_from(&[0xD040_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::Start, + ) + } + + #[test] + fn continue_format() { + assert_eq!( + Packet::try_from(&[0xD080_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::Continue, + ) + } + + #[test] + fn end_format() { + assert_eq!( + Packet::try_from(&[0xD0C0_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::End, + ) + } +} diff --git a/src/flex_data/unknown_metadata_text.rs b/src/flex_data/unknown_metadata_text.rs index aae74d9..376a06c 100644 --- a/src/flex_data/unknown_metadata_text.rs +++ b/src/flex_data/unknown_metadata_text.rs @@ -508,16 +508,16 @@ mod tests { .unwrap(); let mut packets = message.packets(); assert_eq!( - packets.next(), - Some(&[0xD050_0100, 0x4469_6769, 0x7461_6C20, 0x4175_6469,][..]) + &*packets.next().unwrap(), + &[0xD050_0100, 0x4469_6769, 0x7461_6C20, 0x4175_6469,][..], ); assert_eq!( - packets.next(), - Some(&[0xD090_0100, 0x6F20_576F, 0x726B_7374, 0x6174_696F,][..]) + &*packets.next().unwrap(), + &[0xD090_0100, 0x6F20_576F, 0x726B_7374, 0x6174_696F,][..], ); assert_eq!( - packets.next(), - Some(&[0xD0D0_0100, 0x6E20_2D20, 0x4441_5733, 0x362D_3136,][..]) + &*packets.next().unwrap(), + &[0xD0D0_0100, 0x6E20_2D20, 0x4441_5733, 0x362D_3136,][..], ); assert_eq!(packets.next(), None); } diff --git a/src/lib.rs b/src/lib.rs index 6a26059..c3204f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ pub mod error; mod detail; mod message; +mod packet; mod packets; mod traits; diff --git a/src/message.rs b/src/message.rs index ec5eae8..e85ddc8 100644 --- a/src/message.rs +++ b/src/message.rs @@ -263,15 +263,15 @@ mod tests { let message = UmpMessage::try_from(&buffer[..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x3E16_0001, 0x0203_0405,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_0607, 0x0809_0A0B,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_0C0D, 0x0E0F_1011,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_1213, 0x1415_1617,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_1819, 0x1A1B_1C1D,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_1E1F, 0x2021_2223,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_2425, 0x2627_2829,][..])); - assert_eq!(packets.next(), Some(&[0x3E26_2A2B, 0x2C2D_2E2F,][..])); - assert_eq!(packets.next(), Some(&[0x3E32_3031, 0x0000_0000,][..])); + assert_eq!(&*packets.next().unwrap(), &[0x3E16_0001, 0x0203_0405,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_0607, 0x0809_0A0B,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_0C0D, 0x0E0F_1011,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_1213, 0x1415_1617,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_1819, 0x1A1B_1C1D,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_1E1F, 0x2021_2223,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_2425, 0x2627_2829,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E26_2A2B, 0x2C2D_2E2F,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3E32_3031, 0x0000_0000,][..]); assert_eq!(packets.next(), None); } diff --git a/src/packet.rs b/src/packet.rs new file mode 100644 index 0000000..6c91e4f --- /dev/null +++ b/src/packet.rs @@ -0,0 +1,91 @@ +#![doc = include_str!("sysex7/README.md")] + +use crate::{detail::common_err_strings, error::InvalidData}; + +#[derive(Eq, PartialEq, Clone, Debug, derive_more::From)] +pub enum Packet { + ChannelVoice1(crate::channel_voice1::Packet), + ChannelVoice2(crate::channel_voice2::Packet), + FlexData(crate::flex_data::Packet), + Sysex7(crate::sysex7::Packet), + Sysex8(crate::sysex8::Packet), + SystemCommon(crate::system_common::Packet), + UmpStream(crate::ump_stream::Packet), + Utility(crate::utility::Packet), +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + match self { + Self::ChannelVoice1(p) => p.deref(), + Self::ChannelVoice2(p) => p.deref(), + Self::FlexData(p) => p.deref(), + Self::Sysex7(p) => p.deref(), + Self::Sysex8(p) => p.deref(), + Self::SystemCommon(p) => p.deref(), + Self::UmpStream(p) => p.deref(), + Self::Utility(p) => p.deref(), + } + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = crate::error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + use crate::{ + channel_voice1, channel_voice2, detail::BitOps, flex_data, sysex7, sysex8, + system_common, ump_stream, utility, + }; + + if data.is_empty() { + return Err(InvalidData(common_err_strings::ERR_SLICE_TOO_SHORT)); + } + + match u8::from(data[0].nibble(0)) { + channel_voice1::UMP_MESSAGE_TYPE => Ok(channel_voice1::Packet::try_from(data)?.into()), + channel_voice2::UMP_MESSAGE_TYPE => Ok(channel_voice2::Packet::try_from(data)?.into()), + flex_data::UMP_MESSAGE_TYPE => Ok(flex_data::Packet::try_from(data)?.into()), + sysex7::UMP_MESSAGE_TYPE => Ok(sysex7::Packet::try_from(data)?.into()), + sysex8::UMP_MESSAGE_TYPE => Ok(sysex8::Packet::try_from(data)?.into()), + system_common::UMP_MESSAGE_TYPE => Ok(system_common::Packet::try_from(data)?.into()), + ump_stream::UMP_MESSAGE_TYPE => Ok(ump_stream::Packet::try_from(data)?.into()), + utility::UMP_MESSAGE_TYPE => Ok(utility::Packet::try_from(data)?.into()), + _ => Err(crate::error::InvalidData( + common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::ops::Deref; + + #[test] + fn construction() { + let data = [0x0]; + assert_eq!(Packet::try_from(&data[..]).unwrap().deref(), &[0x0]); + } + + #[test] + fn construction_from_empty_data() { + let data = []; + assert_eq!( + Packet::try_from(&data[..]), + Err(InvalidData(common_err_strings::ERR_SLICE_TOO_SHORT)) + ); + } + + #[test] + fn construction_from_reserved_ump_type_field() { + let data = [0xE000_0000, 0x0, 0x0, 0x0]; + assert_eq!( + Packet::try_from(&data[..]), + Err(InvalidData( + common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )) + ); + } +} diff --git a/src/packets.rs b/src/packets.rs index f8e2354..428775e 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -1,3 +1,5 @@ +use crate::packet::Packet; + /// Iterator type for reading the individual packets of a /// [Ump](crate::buffer::Ump) backed message. /// @@ -6,12 +8,12 @@ pub struct PacketsIterator<'a>(pub(crate) core::slice::ChunksExact<'a, u32>); impl<'a> core::iter::Iterator for PacketsIterator<'a> { - type Item = &'a [u32]; + type Item = crate::packet::Packet; fn next(&mut self) -> Option { - self.0.next() + self.0.next().map(|data| Packet::try_from(data).unwrap()) } fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) + self.0.nth(n).map(|data| Packet::try_from(data).unwrap()) } fn count(self) -> usize where @@ -44,9 +46,9 @@ impl<'a> core::iter::ExactSizeIterator for PacketsIterator<'a> { /// /// let mut packets = message.packets(); /// -/// assert_eq!(packets.next(), Some(&[0xD0500101, 0x53686164, 0x6F777320, 0x6F662074][..])); -/// assert_eq!(packets.next(), Some(&[0xD0900101, 0x68652046, 0x6F72676F, 0x7474656E][..])); -/// assert_eq!(packets.next(), Some(&[0xD0D00101, 0x20436174, 0x68656472, 0x616C0000][..])); +/// assert_eq!(&*packets.next().unwrap(), &[0xD0500101, 0x53686164, 0x6F777320, 0x6F662074][..]); +/// assert_eq!(&*packets.next().unwrap(), &[0xD0900101, 0x68652046, 0x6F72676F, 0x7474656E][..]); +/// assert_eq!(&*packets.next().unwrap(), &[0xD0D00101, 0x20436174, 0x68656472, 0x616C0000][..]); /// assert_eq!(packets.next(), None); /// ``` /// @@ -61,10 +63,10 @@ impl<'a> core::iter::ExactSizeIterator for PacketsIterator<'a> { /// /// let mut packets = message.packets(); /// -/// assert_eq!(packets.next(), Some(&[0x30160001, 0x2030405][..])); -/// assert_eq!(packets.next(), Some(&[0x30260607, 0x8090A0B][..])); -/// assert_eq!(packets.next(), Some(&[0x30260C0D, 0xE0F1011][..])); -/// assert_eq!(packets.next(), Some(&[0x30321213, 0x0][..])); +/// assert_eq!(&*packets.next().unwrap(), &[0x30160001, 0x2030405][..]); +/// assert_eq!(&*packets.next().unwrap(), &[0x30260607, 0x8090A0B][..]); +/// assert_eq!(&*packets.next().unwrap(), &[0x30260C0D, 0xE0F1011][..]); +/// assert_eq!(&*packets.next().unwrap(), &[0x30321213, 0x0][..]); /// assert_eq!(packets.next(), None); /// ``` pub trait Packets { diff --git a/src/sysex7.rs b/src/sysex7.rs index b49cfc6..45b8f9e 100644 --- a/src/sysex7.rs +++ b/src/sysex7.rs @@ -6,6 +6,10 @@ use crate::{ ux::{self, u7}, }; +mod packet; + +pub use packet::Packet; + pub(crate) const UMP_MESSAGE_TYPE: u8 = 0x3; #[midi2_proc::generate_message(MinSizeUmp(2), MinSizeBytes(2))] @@ -1633,11 +1637,11 @@ mod tests { let message = Sysex7::try_from(&buffer[..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x3016_0001, 0x0203_0405,][..])); - assert_eq!(packets.next(), Some(&[0x3026_0607, 0x0809_0A0B,][..])); - assert_eq!(packets.next(), Some(&[0x3026_0C0D, 0x0E0F_1011,][..])); - assert_eq!(packets.next(), Some(&[0x3026_1213, 0x1415_1617,][..])); - assert_eq!(packets.next(), Some(&[0x3036_1819, 0x1A1B_1C1D,][..])); + assert_eq!(&*packets.next().unwrap(), &[0x3016_0001, 0x0203_0405,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3026_0607, 0x0809_0A0B,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3026_0C0D, 0x0E0F_1011,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3026_1213, 0x1415_1617,][..]); + assert_eq!(&*packets.next().unwrap(), &[0x3036_1819, 0x1A1B_1C1D,][..]); assert_eq!(packets.next(), None); } @@ -1648,7 +1652,7 @@ mod tests { let message = Sysex7::<[u32; 2]>::new(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x3000_0000, 0x0][..])); + assert_eq!(&*packets.next().unwrap(), &[0x3000_0000, 0x0][..]); assert_eq!(packets.next(), None); } } diff --git a/src/sysex7/packet.rs b/src/sysex7/packet.rs new file mode 100644 index 0000000..1f7818e --- /dev/null +++ b/src/sysex7/packet.rs @@ -0,0 +1,162 @@ +use crate::{error, sysex7}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 2]); + +impl crate::traits::BufferAccess<[u32; 2]> for Packet { + fn buffer_access(&self) -> &[u32; 2] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 2] + where + [u32; 2]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.len() < 2 { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != sysex7::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + status_from_data(data)?; + + Ok(Packet({ + let mut buffer = [0x0; 2]; + buffer[..data.len()].copy_from_slice(data); + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +impl crate::Grouped<[u32; 2]> for Packet { + fn group(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(1) + } + fn set_group(&mut self, group: crate::ux::u4) + where + [u32; 2]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(1, group); + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Status { + Complete, + Start, + Continue, + End, +} + +impl Packet { + pub fn status(&self) -> Status { + status_from_data(&self.0[..]).unwrap() + } +} + +fn status_from_data(data: &[u32]) -> Result { + use crate::detail::BitOps; + use Status::*; + match u8::from(data[0].nibble(2)) { + 0x0 => Ok(Complete), + 0x1 => Ok(Start), + 0x2 => Ok(Continue), + 0x3 => Ok(End), + _ => Err(error::InvalidData("Invalid SysEx7 status byte")), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn construction() { + assert_eq!( + &*Packet::try_from(&[0x3000_0000, 0x0000_0000][..]).unwrap(), + &[0x3000_0000, 0x0000_0000][..], + ); + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[0x3000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ); + } + + #[test] + fn construction_wrong_ump_type() { + assert_eq!( + Packet::try_from(&[0x0000_0000, 0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ); + } + + #[test] + fn construction_complete() { + assert_eq!( + Packet::try_from(&[0x3000_0000, 0x0000_0000][..]) + .unwrap() + .status(), + Status::Complete, + ); + } + + #[test] + fn construction_start() { + assert_eq!( + Packet::try_from(&[0x3010_0000, 0x0000_0000][..]) + .unwrap() + .status(), + Status::Start, + ); + } + + #[test] + fn construction_continue() { + assert_eq!( + Packet::try_from(&[0x3020_0000, 0x0000_0000][..]) + .unwrap() + .status(), + Status::Continue, + ); + } + + #[test] + fn construction_end() { + assert_eq!( + Packet::try_from(&[0x3030_0000, 0x0000_0000][..]) + .unwrap() + .status(), + Status::End, + ); + } +} diff --git a/src/sysex8.rs b/src/sysex8.rs index 50ebc86..25d95e5 100644 --- a/src/sysex8.rs +++ b/src/sysex8.rs @@ -4,6 +4,10 @@ use crate::{ ux, }; +mod packet; + +pub use packet::Packet; + pub(crate) const UMP_MESSAGE_TYPE: u8 = 0x5; const ERR_INVALID_NUMBER_OF_PAYLOAD_BYTES: &str = "Invalid number of payload bytes in packet"; @@ -1226,20 +1230,20 @@ mod tests { let mut packets = message.packets(); assert_eq!( - packets.next(), - Some(&[0x501E_0000, 0x0102_0304, 0x0506_0708, 0x090A_0B0C,][..]) + &*packets.next().unwrap(), + &[0x501E_0000, 0x0102_0304, 0x0506_0708, 0x090A_0B0C,][..] ); assert_eq!( - packets.next(), - Some(&[0x502E_000D, 0x0E0F_1011, 0x1213_1415, 0x1617_1819,][..]) + &*packets.next().unwrap(), + &[0x502E_000D, 0x0E0F_1011, 0x1213_1415, 0x1617_1819,][..] ); assert_eq!( - packets.next(), - Some(&[0x502E_001A, 0x1B1C_1D1E, 0x1F20_2122, 0x2324_2526,][..]) + &*packets.next().unwrap(), + &[0x502E_001A, 0x1B1C_1D1E, 0x1F20_2122, 0x2324_2526,][..] ); assert_eq!( - packets.next(), - Some(&[0x503C_0027, 0x2829_2A2B, 0x2C2D_2E2F, 0x3031_0000,][..]) + &*packets.next().unwrap(), + &[0x503C_0027, 0x2829_2A2B, 0x2C2D_2E2F, 0x3031_0000,][..] ); assert_eq!(packets.next(), None); } @@ -1250,7 +1254,7 @@ mod tests { let message = Sysex8::<[u32; 4]>::new(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x5001_0000, 0x0, 0x0, 0x0][..])); + assert_eq!(&*packets.next().unwrap(), &[0x5001_0000, 0x0, 0x0, 0x0][..]); assert_eq!(packets.next(), None); } } diff --git a/src/sysex8/packet.rs b/src/sysex8/packet.rs new file mode 100644 index 0000000..24f9783 --- /dev/null +++ b/src/sysex8/packet.rs @@ -0,0 +1,225 @@ +use crate::{error, sysex8}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 4]); + +impl crate::traits::BufferAccess<[u32; 4]> for Packet { + fn buffer_access(&self) -> &[u32; 4] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 4] + where + [u32; 4]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.len() < 4 { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != sysex8::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + status_from_data(data)?; + + Ok(Packet({ + let mut buffer = [0x0; 4]; + buffer[..data.len()].copy_from_slice(data); + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +impl crate::Grouped<[u32; 4]> for Packet { + fn group(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(1) + } + fn set_group(&mut self, group: crate::ux::u4) + where + [u32; 4]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(1, group); + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum PreviousDataValidity { + Valid, + Invalid, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Status { + Complete, + Start, + Continue, + End, + UnexpectedEnd(PreviousDataValidity), +} + +fn status_from_data(data: &[u32]) -> Result { + use crate::detail::BitOps; + use PreviousDataValidity::*; + use Status::*; + match u8::from(data[0].nibble(2)) { + 0x0 => Ok(Complete), + 0x1 => Ok(Start), + 0x2 => Ok(Continue), + 0x3 => { + if data[0].octet(3) == 0 && data[1..4].iter().all(|b| *b == 0x0) { + // unexpected end + match u8::from(data[0].nibble(3)) { + 0x1 => Ok(UnexpectedEnd(Valid)), + 0xF => Ok(UnexpectedEnd(Invalid)), + _ => Ok(End), + } + } else { + Ok(End) + } + } + _ => Err(error::InvalidData("Invalid SysEx8 status byte")), + } +} + +impl Packet { + pub fn status(&self) -> Status { + status_from_data(&self.0[..]).unwrap() + } + pub fn stream_id(&self) -> u8 { + sysex8::stream_id_from_packet(&self.0[..]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn construction() { + assert!( + Packet::try_from(&[0x5001_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]).is_ok() + ); + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[0x5001_0000, 0x0000_0000, 0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ); + } + + #[test] + fn construction_incorrect_ump_type() { + assert_eq!( + Packet::try_from(&[0x0001_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ); + } + + #[test] + fn complete() { + assert_eq!( + Packet::try_from(&[0x5001_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .status(), + Status::Complete, + ); + } + + #[test] + fn start() { + assert_eq!( + Packet::try_from(&[0x5011_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .status(), + Status::Start, + ); + } + + #[test] + fn cont() { + assert_eq!( + Packet::try_from(&[0x5021_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .status(), + Status::Continue, + ); + } + + #[test] + fn end() { + assert_eq!( + Packet::try_from(&[0x5032_0001, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .status(), + Status::End, + ); + } + + #[test] + fn unexpected_end_valid_data() { + assert_eq!( + Packet::try_from(&[0x5031_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .status(), + Status::UnexpectedEnd(PreviousDataValidity::Valid), + ); + } + + #[test] + fn unexpected_end_invalid_data() { + assert_eq!( + Packet::try_from(&[0x503F_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .status(), + Status::UnexpectedEnd(PreviousDataValidity::Invalid), + ); + } + + #[test] + fn stream_id() { + assert_eq!( + Packet::try_from(&[0x5001_0100, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .stream_id(), + 0x1, + ); + } + + #[test] + fn group() { + use crate::Grouped; + + assert_eq!( + Packet::try_from(&[0x5A01_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000,][..]) + .unwrap() + .group(), + crate::num::u4::new(0xA), + ); + } +} diff --git a/src/system_common.rs b/src/system_common.rs index 42c4330..8662385 100644 --- a/src/system_common.rs +++ b/src/system_common.rs @@ -2,6 +2,7 @@ pub(crate) const UMP_MESSAGE_TYPE: u8 = 0x1; +mod packet; mod song_position_pointer; mod song_select; mod time_code; @@ -177,6 +178,7 @@ mod reset { pub use active_sensing::*; pub use cont::*; +pub use packet::Packet; pub use reset::*; pub use song_position_pointer::*; pub use song_select::*; @@ -331,7 +333,7 @@ mod tests { let message = SystemCommon::try_from(&[0x15F1_5F00_u32][..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x15F1_5F00_u32][..])); + assert_eq!(&*packets.next().unwrap(), &[0x15F1_5F00_u32][..]); assert_eq!(packets.next(), None); } diff --git a/src/system_common/packet.rs b/src/system_common/packet.rs new file mode 100644 index 0000000..f4a6f42 --- /dev/null +++ b/src/system_common/packet.rs @@ -0,0 +1,101 @@ +use crate::{error, system_common}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 1]); + +impl crate::traits::BufferAccess<[u32; 1]> for Packet { + fn buffer_access(&self) -> &[u32; 1] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 1] + where + [u32; 1]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.is_empty() { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != system_common::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + Ok(Packet({ + let mut buffer = [0x0; 1]; + buffer[0] = data[0]; + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +impl crate::Grouped<[u32; 1]> for Packet { + fn group(&self) -> crate::ux::u4 { + use crate::detail::BitOps; + self.0[0].nibble(1) + } + fn set_group(&mut self, group: crate::ux::u4) + where + [u32; 1]: crate::buffer::BufferMut, + { + use crate::detail::BitOps; + self.0[0].set_nibble(1, group); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn construction() { + assert!(Packet::try_from(&[0x1000_0000][..]).is_ok()); + } + + #[test] + fn construction_incorrect_ump_message_type() { + assert_eq!( + Packet::try_from(&[0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ); + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ); + } + + #[test] + fn group() { + use crate::Grouped; + assert_eq!( + Packet::try_from(&[0x1A00_0000][..]).unwrap().group(), + crate::num::u4::new(0xA), + ); + } +} diff --git a/src/system_common/song_position_pointer.rs b/src/system_common/song_position_pointer.rs index f417a00..ea23dd6 100644 --- a/src/system_common/song_position_pointer.rs +++ b/src/system_common/song_position_pointer.rs @@ -87,7 +87,7 @@ mod tests { let message = SongPositionPointer::try_from(&[0x1AF2_7D6C][..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x1AF2_7D6C][..])); + assert_eq!(&*packets.next().unwrap(), &[0x1AF2_7D6C][..]); assert_eq!(packets.next(), None); } } diff --git a/src/ump_stream.rs b/src/ump_stream.rs index 787041d..528f84e 100644 --- a/src/ump_stream.rs +++ b/src/ump_stream.rs @@ -11,6 +11,7 @@ mod endpoint_name; mod function_block_discovery; mod function_block_info; mod function_block_name; +mod packet; mod product_instance_id; mod start_of_clip; mod stream_configuration_notification; @@ -24,6 +25,7 @@ pub use endpoint_name::*; pub use function_block_discovery::*; pub use function_block_info::*; pub use function_block_name::FunctionBlockName; +pub use packet::{Format, Packet}; pub use product_instance_id::*; pub use start_of_clip::*; pub use stream_configuration_notification::*; @@ -69,7 +71,9 @@ impl<'a> TryFrom<&'a [u32]> for UmpStream<&'a [u32]> { fn try_from(value: &'a [u32]) -> Result { use UmpStream::*; if value.is_empty() { - return Err(crate::error::InvalidData("Slice is too short")); + return Err(crate::error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); }; Ok(match status_from_buffer(value) { device_identity::STATUS => { @@ -511,20 +515,20 @@ mod tests { let mut packets = message.packets(); assert_eq!( - packets.next(), - Some(&[0xF403_5268, 0x7974_686D, 0x5265_7665, 0x6C61_7469,][..]) + &*packets.next().unwrap(), + &[0xF403_5268, 0x7974_686D, 0x5265_7665, 0x6C61_7469,][..], ); assert_eq!( - packets.next(), - Some(&[0xF803_6F6E, 0x3A20_4265, 0x6174_7320, 0x4265_796F,][..]) + &*packets.next().unwrap(), + &[0xF803_6F6E, 0x3A20_4265, 0x6174_7320, 0x4265_796F,][..], ); assert_eq!( - packets.next(), - Some(&[0xF803_6E64, 0x2042_6F75, 0x6E64_6172, 0x6965_73F0,][..]) + &*packets.next().unwrap(), + &[0xF803_6E64, 0x2042_6F75, 0x6E64_6172, 0x6965_73F0,][..], ); assert_eq!( - packets.next(), - Some(&[0xFC03_9F8C, 0x8DF0_9FA5, 0x81F0_9F9A, 0x8000_0000,][..]) + &*packets.next().unwrap(), + &[0xFC03_9F8C, 0x8DF0_9FA5, 0x81F0_9F9A, 0x8000_0000,][..], ); assert_eq!(packets.next(), None,); } diff --git a/src/ump_stream/device_identity.rs b/src/ump_stream/device_identity.rs index 6774bce..3718afd 100644 --- a/src/ump_stream/device_identity.rs +++ b/src/ump_stream/device_identity.rs @@ -129,8 +129,8 @@ mod tests { let mut packets = message.packets(); assert_eq!( - packets.next(), - Some(&[0xF002_0000, 0x000F_3328, 0x4A1E_1870, 0x4354_3201][..]) + &*packets.next().unwrap(), + &[0xF002_0000, 0x000F_3328, 0x4A1E_1870, 0x4354_3201][..], ); assert_eq!(packets.next(), None); } diff --git a/src/ump_stream/endpoint_name.rs b/src/ump_stream/endpoint_name.rs index c9f2477..422bb5f 100644 --- a/src/ump_stream/endpoint_name.rs +++ b/src/ump_stream/endpoint_name.rs @@ -127,12 +127,12 @@ mod tests { let mut packets = message.packets(); assert_eq!( - packets.next(), - Some(&[0xF403_4769, 0x6D6D_6520, 0x736F_6D65, 0x2073_6967,][..]) + &*packets.next().unwrap(), + &[0xF403_4769, 0x6D6D_6520, 0x736F_6D65, 0x2073_6967,][..], ); assert_eq!( - packets.next(), - Some(&[0xFC03_6E61, 0x6C20_F09F, 0x948A_20F0, 0x9F99_8C00,][..]) + &*packets.next().unwrap(), + &[0xFC03_6E61, 0x6C20_F09F, 0x948A_20F0, 0x9F99_8C00,][..], ); assert_eq!(packets.next(), None); } diff --git a/src/ump_stream/packet.rs b/src/ump_stream/packet.rs new file mode 100644 index 0000000..8859ebd --- /dev/null +++ b/src/ump_stream/packet.rs @@ -0,0 +1,144 @@ +use crate::{error, ump_stream}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 4]); + +impl crate::traits::BufferAccess<[u32; 4]> for Packet { + fn buffer_access(&self) -> &[u32; 4] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 4] + where + [u32; 4]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.is_empty() { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != ump_stream::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + Ok(Packet({ + let mut buffer = [0x0; 4]; + buffer[..data.len()].copy_from_slice(data); + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Format { + Complete, + Start, + Continue, + End, +} + +impl Packet { + pub fn format(&self) -> Format { + format_from_data(&self.0[..]) + } +} + +fn format_from_data(data: &[u32]) -> Format { + use crate::detail::BitOps; + use Format::*; + match u8::from(data[0].crumb(2)) { + 0x0 => Complete, + 0x1 => Start, + 0x2 => Continue, + 0x3 => End, + _ => unreachable!(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn construction() { + assert!(Packet::try_from(&[0xF000_0000][..]).is_ok()) + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ) + } + + #[test] + fn construction_incorrect_ump_message_type() { + assert_eq!( + Packet::try_from(&[0x0000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ) + } + + #[test] + fn complete_format() { + assert_eq!( + Packet::try_from(&[0xF000_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::Complete, + ) + } + + #[test] + fn start_format() { + assert_eq!( + Packet::try_from(&[0xF400_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::Start, + ) + } + + #[test] + fn continue_format() { + assert_eq!( + Packet::try_from(&[0xF800_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::Continue, + ) + } + + #[test] + fn end_format() { + assert_eq!( + Packet::try_from(&[0xFC00_0000, 0x0000_0000, 0x0000_0000, 0x0000_0000][..]) + .unwrap() + .format(), + Format::End, + ) + } +} diff --git a/src/utility.rs b/src/utility.rs index 81ce236..06d44bf 100644 --- a/src/utility.rs +++ b/src/utility.rs @@ -129,6 +129,7 @@ pub use clock::Clock; pub use delta_clockstamp::DeltaClockstamp; pub use delta_clockstamp_tpq::DeltaClockstampTpq; pub use no_op::NoOp; +pub use packet::Packet; pub use timestamp::Timestamp; struct DataProperty; @@ -187,7 +188,9 @@ impl<'a> core::convert::TryFrom<&'a [u32]> for Utility<&'a [u32]> { type Error = crate::error::InvalidData; fn try_from(buffer: &'a [u32]) -> Result { if buffer.is_empty() { - return Err(crate::error::InvalidData("Slice is too short")); + return Err(crate::error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); }; Ok(match status(buffer) { no_op::STATUS => no_op::NoOp::try_from(buffer)?.into(), @@ -215,6 +218,7 @@ fn status(buffer: &[U]) -> u8 { } .into() } +mod packet; #[cfg(test)] mod tests { @@ -238,7 +242,7 @@ mod tests { let message = Utility::try_from(&[0x0010_1234][..]).unwrap(); let mut packets = message.packets(); - assert_eq!(packets.next(), Some(&[0x0010_1234][..])); + assert_eq!(&*packets.next().unwrap(), &[0x0010_1234][..]); assert_eq!(packets.next(), None); } diff --git a/src/utility/packet.rs b/src/utility/packet.rs new file mode 100644 index 0000000..422ec5d --- /dev/null +++ b/src/utility/packet.rs @@ -0,0 +1,78 @@ +use crate::{error, utility}; + +#[derive(Eq, PartialEq, Clone, midi2_proc::Debug)] +pub struct Packet(pub(crate) [u32; 1]); + +impl crate::traits::BufferAccess<[u32; 1]> for Packet { + fn buffer_access(&self) -> &[u32; 1] { + &self.0 + } + fn buffer_access_mut(&mut self) -> &mut [u32; 1] + where + [u32; 1]: crate::buffer::BufferMut, + { + &mut self.0 + } +} + +impl<'a> core::convert::TryFrom<&'a [u32]> for Packet { + type Error = error::InvalidData; + fn try_from(data: &'a [u32]) -> Result { + if data.is_empty() { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT, + )); + } + + use crate::detail::BitOps; + if u8::from(data[0].nibble(0)) != utility::UMP_MESSAGE_TYPE { + return Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE, + )); + } + + Ok(Packet({ + let mut buffer = [0x0; 1]; + buffer[0] = data[0]; + buffer + })) + } +} + +impl core::ops::Deref for Packet { + type Target = [u32]; + fn deref(&self) -> &Self::Target { + &self.0[..] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn construction() { + assert!(Packet::try_from(&[0x0000_0000][..]).is_ok()); + } + + #[test] + fn construction_incorrect_ump_message_type() { + assert_eq!( + Packet::try_from(&[0x1000_0000][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_INCORRECT_UMP_MESSAGE_TYPE + )), + ); + } + + #[test] + fn construction_short_slice() { + assert_eq!( + Packet::try_from(&[][..]), + Err(error::InvalidData( + crate::detail::common_err_strings::ERR_SLICE_TOO_SHORT + )), + ); + } +}