Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  packets.rs   Sprache: unbekannt

 
use coremidi_sys::MIDIPacketList;
use coremidi_sys::{MIDIPacket, MIDIPacketNext, MIDITimeStamp};

use std::fmt;
use std::ops::{Deref, DerefMut};
use std::slice;

pub type Timestamp = u64;

const MAX_PACKET_DATA_LENGTH: usize = 0xffffusize;

#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
pub mod alignment {
    pub type Marker = [u32; 0]; // ensures 4-byte alignment (on ARM)
    pub const NEEDS_ALIGNMENT: bool = true;
}

#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
pub mod alignment {
    pub type Marker = [u8; 0]; // unaligned
    pub const NEEDS_ALIGNMENT: bool = false;
}

/// A collection of simultaneous MIDI events.
/// See [MIDIPacket](https://developer.apple.com/reference/coremidi/midipacket).
///
#[repr(C)]
pub struct Packet {
    // NOTE: At runtime this type must only be used behind immutable references
    //       that point to valid instances of MIDIPacket (mutable references would allow mem::swap).
    //       This type must NOT implement `Copy`!
    //       On ARM, this must be 4-byte aligned.
    inner: PacketInner,
    _alignment_marker: alignment::Marker,
}

#[repr(packed)]
struct PacketInner {
    timestamp: MIDITimeStamp,
    length: u16,
    data: [u8; 0], // zero-length, because we cannot make this type bigger without knowing how much data there actually is
}

impl Packet {
    /// Get the packet timestamp.
    ///
    pub fn timestamp(&self) -> Timestamp {
        self.inner.timestamp as Timestamp
    }

    /// Get the packet data. This method just gives raw MIDI bytes. You would need another
    /// library to decode them and work with higher level events.
    ///
    ///
    /// The following example:
    ///
    /// ```
    /// let packet_list = &coremidi::PacketBuffer::new(0, &[0x90, 0x40, 0x7f]);
    /// for packet in packet_list.iter() {
    ///   for byte in packet.data() {
    ///     print!(" {:x}", byte);
    ///   }
    /// }
    /// ```
    ///
    /// will print:
    ///
    /// ```text
    ///  90 40 7f
    /// ```
    pub fn data(&self) -> &[u8] {
        let data_ptr = self.inner.data.as_ptr();
        let data_len = self.inner.length as usize;
        unsafe { slice::from_raw_parts(data_ptr, data_len) }
    }
}

impl fmt::Debug for Packet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = write!(
            f,
            "Packet(ptr={:x}, ts={:016x}, data=[",
            self as *const _ as usize,
            self.timestamp() as u64
        );
        let result = self
            .data()
            .iter()
            .enumerate()
            .fold(result, |prev_result, (i, b)| match prev_result {
                Err(err) => Err(err),
                Ok(()) => {
                    let sep = if i > 0 { ", " } else { "" };
                    write!(f, "{}{:02x}", sep, b)
                }
            });
        result.and_then(|_| write!(f, "])"))
    }
}

impl fmt::Display for Packet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = write!(f, "{:016x}:", self.timestamp());
        self.data()
            .iter()
            .fold(result, |prev_result, b| match prev_result {
                Err(err) => Err(err),
                Ok(()) => write!(f, " {:02x}", b),
            })
    }
}

/// A [list of MIDI events](https://developer.apple.com/reference/coremidi/midipacketlist) being received from, or being sent to, one endpoint.
///
#[repr(C)]
pub struct PacketList {
    // NOTE: This type must only exist in the form of immutable references
    //       pointing to valid instances of MIDIPacketList.
    //       This type must NOT implement `Copy`!
    inner: PacketListInner,
    _do_not_construct: alignment::Marker,
}

#[repr(packed)]
struct PacketListInner {
    num_packets: u32,
    data: [MIDIPacket; 0],
}

impl PacketList {
    /// For internal usage only.
    /// Requires this instance to actually point to a valid MIDIPacketList
    pub(crate) unsafe fn as_ptr(&self) -> *mut MIDIPacketList {
        self as *const PacketList as *mut PacketList as *mut MIDIPacketList
    }
}

impl PacketList {
    /// Check if the packet list is empty.
    ///
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get the number of packets in the list.
    ///
    pub fn len(&self) -> usize {
        self.inner.num_packets as usize
    }

    /// Get an iterator for the packets in the list.
    ///
    pub fn iter(&self) -> PacketListIterator {
        PacketListIterator {
            count: self.len(),
            packet_ptr: std::ptr::addr_of!(self.inner.data) as *const MIDIPacket,
            _phantom: ::std::marker::PhantomData::default(),
        }
    }
}

impl fmt::Debug for PacketList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let result = write!(f, "PacketList(ptr={:x}, packets=[", unsafe {
            self.as_ptr() as usize
        });
        self.iter()
            .enumerate()
            .fold(result, |prev_result, (i, packet)| match prev_result {
                Err(err) => Err(err),
                Ok(()) => {
                    let sep = if i != 0 { ", " } else { "" };
                    write!(f, "{}{:?}", sep, packet)
                }
            })
            .and_then(|_| write!(f, "])"))
    }
}

impl fmt::Display for PacketList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let num_packets = self.inner.num_packets;
        let result = write!(f, "PacketList(len={})", num_packets);
        self.iter()
            .fold(result, |prev_result, packet| match prev_result {
                Err(err) => Err(err),
                Ok(()) => write!(f, "\n  {}", packet),
            })
    }
}

pub struct PacketListIterator<'a> {
    count: usize,
    packet_ptr: *const MIDIPacket,
    _phantom: ::std::marker::PhantomData<&'a Packet>,
}

impl<'a> Iterator for PacketListIterator<'a> {
    type Item = &'a Packet;

    fn next(&mut self) -> Option<&'a Packet> {
        if self.count > 0 {
            let packet = unsafe { &*(self.packet_ptr as *const Packet) };
            self.count -= 1;
            self.packet_ptr = unsafe { MIDIPacketNext(self.packet_ptr) };
            Some(packet)
        } else {
            None
        }
    }
}

const PACKET_LIST_HEADER_SIZE: usize = 4; // MIDIPacketList::numPackets: UInt32
const PACKET_HEADER_SIZE: usize = 8 +      // MIDIPacket::timeStamp: MIDITimeStamp/UInt64
                                  2; // MIDIPacket::length: UInt16

const INLINE_PACKET_BUFFER_SIZE: usize = 28; // must be divisible by 4

enum PacketBufferStorage {
    /// Inline stores the data directy on the stack, if it is small enough.
    /// NOTE: using u32 ensures correct alignment (required on ARM)
    Inline([u32; INLINE_PACKET_BUFFER_SIZE / 4]),
    /// External is used whenever the size of the data exceeds INLINE_PACKET_BUFFER_SIZE.
    /// This means that the size of the contained vector is always greater than INLINE_PACKET_BUFFER_SIZE.
    External(Vec<u32>),
}

impl PacketBufferStorage {
    #[inline]
    pub fn with_capacity(capacity: usize) -> PacketBufferStorage {
        if capacity <= INLINE_PACKET_BUFFER_SIZE {
            PacketBufferStorage::Inline([0; INLINE_PACKET_BUFFER_SIZE / 4])
        } else {
            let u32_len = ((capacity - 1) / 4) + 1;
            let mut buffer = Vec::with_capacity(u32_len);
            unsafe {
                buffer.set_len(u32_len);
            }
            PacketBufferStorage::External(buffer)
        }
    }

    #[inline]
    fn capacity(&self) -> usize {
        match *self {
            PacketBufferStorage::Inline(ref inline) => inline.len() * 4,
            PacketBufferStorage::External(ref vec) => vec.len() * 4,
        }
    }

    #[inline]
    fn get_slice(&self) -> &[u8] {
        unsafe {
            match *self {
                PacketBufferStorage::Inline(ref inline) => {
                    slice::from_raw_parts(inline.as_ptr() as *const u8, inline.len() * 4)
                }
                PacketBufferStorage::External(ref vec) => {
                    slice::from_raw_parts(vec.as_ptr() as *const u8, vec.len() * 4)
                }
            }
        }
    }

    #[inline]
    fn get_slice_mut(&mut self) -> &mut [u8] {
        unsafe {
            match *self {
                PacketBufferStorage::Inline(ref mut inline) => {
                    slice::from_raw_parts_mut(inline.as_mut_ptr() as *mut u8, inline.len() * 4)
                }
                PacketBufferStorage::External(ref mut vec) => {
                    slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, vec.len() * 4)
                }
            }
        }
    }

    unsafe fn assign_packet(&mut self, packet_offset: usize, time: MIDITimeStamp, data: &[u8]) ;{
        assert!(data.len() <= MAX_PACKET_DATA_LENGTH, "packet data too long"); // cannot store longer size in u16

        if alignment::NEEDS_ALIGNMENT {
            debug_assert!(packet_offset & 0b11 == 0);
        }

        let slice = self.get_slice_mut();
        let ptr = slice[packet_offset..].as_mut_ptr() as *mut Packet;
        (*ptr).inner.timestamp = time;
        (*ptr).inner.length = data.len() as u16;
        let packet_data_start = packet_offset + PACKET_HEADER_SIZE;
        slice[packet_data_start..(packet_data_start + data.len())].copy_from_slice(data);
    }

    /// Requires that there is a valid Packet at `offset`, which has enough space for `data`
    unsafe fn extend_packet(&mut self, packet_offset: usize, data: &[u8]) {
        let slice = self.get_slice_mut();
        let ptr = slice[packet_offset..].as_mut_ptr() as *mut Packet;
        let packet_data_start = packet_offset + PACKET_HEADER_SIZE + (*ptr).inner.length as usize;
        (*ptr).inner.length += data.len() as u16;
        slice[packet_data_start..(packet_data_start + data.len())].copy_from_slice(data);
    }

    /// Call this only with larger length values (won't make the buffer smaller)
    unsafe fn ensure_capacity(&mut self, capacity: usize) {
        if capacity < INLINE_PACKET_BUFFER_SIZE || capacity < self.get_slice().len() {
            return;
        }

        let vec_capacity = ((capacity - 1) / 4) + 1;
        let vec: Option<Vec<u32>> = match *self {
            PacketBufferStorage::Inline(ref inline) => {
                let mut v = Vec::with_capacity(vec_capacity);
                v.extend_from_slice(inline);
                v.set_len(vec_capacity);
                Some(v)
            }
            PacketBufferStorage::External(ref mut vec) => {
                let current_len = vec.len();
                vec.reserve(vec_capacity - current_len);
                vec.set_len(vec_capacity);
                None
            }
        };

        // to prevent borrowcheck errors, this must come after the `match`
        if let Some(v) = vec {
            *self = PacketBufferStorage::External(v);
        }
    }
}

impl Deref for PacketBufferStorage {
    type Target = PacketList;

    #[inline]
    fn deref(&self) -> &PacketList {
        unsafe { &*(self.get_slice().as_ptr() as *const PacketList) }
    }
}

impl DerefMut for PacketBufferStorage {
    // NOTE: Mutable references `&mut PacketList` must not be exposed in the public API!
    //       The user could use mem::swap to modify the header without modifying the packets that follow.
    #[inline]
    fn deref_mut(&mut self) -> &mut PacketList {
        unsafe { &mut *(self.get_slice_mut().as_mut_ptr() as *mut PacketList) }
    }
}

/// A mutable `PacketList` builder.
///
/// A `PacketList` is an inmmutable reference to a [MIDIPacketList](https://developer.apple.com/reference/coremidi/midipacketlist) structure,
/// while a `PacketBuffer` is a mutable structure that allows to build a `PacketList` by adding packets.
/// It dereferences to a `PacketList`, so it can be used whenever a `PacketList` is needed.
///
pub struct PacketBuffer {
    storage: PacketBufferStorage,
    last_packet_offset: usize,
}

impl Deref for PacketBuffer {
    type Target = PacketList;

    #[inline]
    fn deref(&self) -> &PacketList {
        self.storage.deref()
    }
}

impl PacketBuffer {
    /// Create a `PacketBuffer` with a single packet containing the provided timestamp and data.
    ///
    /// According to the official documentation for CoreMIDI, the timestamp represents
    /// the time at which the events are to be played, where zero means "now".
    /// The timestamp applies to the first MIDI byte in the packet.
    ///
    /// Example on how to create a `PacketBuffer` with a single packet for a MIDI note on for C-5:
    ///
    /// ```
    /// let buffer = coremidi::PacketBuffer::new(0, &[0x90, 0x3c, 0x7f]);
    /// assert_eq!(buffer.len(), 1)
    /// ```
    pub fn new(time: MIDITimeStamp, data: &[u8]) -> PacketBuffer {
        let capacity = data.len() + PACKET_LIST_HEADER_SIZE + PACKET_HEADER_SIZE;
        let mut storage = PacketBufferStorage::with_capacity(capacity);
        storage.deref_mut().inner.num_packets = 1;
        let last_packet_offset = PACKET_LIST_HEADER_SIZE;
        unsafe {
            storage.assign_packet(last_packet_offset, time, data);
        }

        PacketBuffer {
            storage,
            last_packet_offset,
        }
    }

    /// Create an empty `PacketBuffer` with no packets.
    ///
    /// Example on how to create an empty `PacketBuffer`
    /// with a capacity for 128 bytes in total (including headers):
    ///
    /// ```
    /// let buffer = coremidi::PacketBuffer::with_capacity(128);
    /// assert_eq!(buffer.len(), 0);
    /// assert_eq!(buffer.capacity(), 128);
    /// ```
    pub fn with_capacity(capacity: usize) -> PacketBuffer {
        let capacity = std::cmp::max(capacity, INLINE_PACKET_BUFFER_SIZE);
        let mut storage = PacketBufferStorage::with_capacity(capacity);
        storage.deref_mut().inner.num_packets = 0;

        PacketBuffer {
            storage,
            last_packet_offset: PACKET_LIST_HEADER_SIZE,
        }
    }

    /// Get underlying buffer capacity in bytes
    pub fn capacity(&self) -> usize {
        self.storage.capacity()
    }

    /// Add a new event containing the provided timestamp and data.
    ///
    /// According to the official documentation for CoreMIDI, the timestamp represents
    /// the time at which the events are to be played, where zero means "now".
    /// The timestamp applies to the first MIDI byte in the packet.
    ///
    /// An event must not have a timestamp that is smaller than that of a previous event
    /// in the same `PacketList`
    ///
    /// Example:
    ///
    /// ```
    /// let mut chord = coremidi::PacketBuffer::new(0, &[0x90, 0x3c, 0x7f]);
    /// chord.push_data(0, &[0x90, 0x40, 0x7f]);
    /// assert_eq!(chord.len(), 1);
    /// let repr = format!("{}", &chord as &coremidi::PacketList);
    /// assert_eq!(repr, "PacketList(len=1)\n  0000000000000000: 90 3c 7f 90 40 7f");
    /// ```
    pub fn push_data(&mut self, time: MIDITimeStamp, data: &[u8]) -> &mut Self {
        let (can_merge, previous_data_len) = self.can_merge_into_last_packet(time, data);

        if can_merge {
            let new_packet_size = Self::packet_size(previous_data_len + data.len());
            unsafe {
                self.storage
                    .ensure_capacity(self.last_packet_offset + new_packet_size);
                self.storage.extend_packet(self.last_packet_offset, data);
            }
        } else {
            let packet_size = Self::packet_size(data.len());
            let next_offset = self.next_packet_offset();
            unsafe {
                self.storage.ensure_capacity(next_offset + packet_size);
                self.storage.assign_packet(next_offset, time, data);
            }
            self.packet_list_mut().num_packets += 1;
            self.last_packet_offset = next_offset;
        }

        self
    }

    /// Clears the buffer, removing all packets.
    /// Note that this method has no effect on the allocated capacity of the buffer.
    pub fn clear(&mut self) {
        self.packet_list_mut().num_packets = 0;
        self.last_packet_offset = PACKET_LIST_HEADER_SIZE;
    }

    /// Checks whether the given tiemstamped data can be merged into the previous packet
    fn can_merge_into_last_packet(&self, time: MIDITimeStamp, data: &[u8]) -> (bool, usize) {
        if self.packet_list_is_empty() {
            (false, 0)
        } else {
            let previous_packet = self.last_packet();
            let previous_packet_data = previous_packet.data();
            let previous_data_len = previous_packet_data.len();
            let can_merge = previous_packet.timestamp() == time
                && Self::not_sysex(data)
                && Self::has_status_byte(data)
                && Self::not_sysex(previous_packet_data)
                && Self::has_status_byte(previous_packet_data)
                && previous_data_len + data.len() < MAX_PACKET_DATA_LENGTH;

            (can_merge, previous_data_len)
        }
    }

    #[inline]
    fn last_packet(&self) -> &Packet {
        assert!(self.packet_list().num_packets > 0);
        let packets_slice = self.storage.get_slice();
        let packet_slot = &packets_slice[self.last_packet_offset..];
        unsafe { &*(packet_slot.as_ptr() as *const Packet) }
    }

    #[inline]
    fn next_packet_offset(&self) -> usize {
        if self.packet_list_is_empty() {
            self.last_packet_offset
        } else {
            let data_len = self.last_packet().inner.length as usize;
            let next_offset = self.last_packet_offset + Self::packet_size(data_len);
            if alignment::NEEDS_ALIGNMENT {
                (next_offset + 3) & !(3usize)
            } else {
                next_offset
            }
        }
    }

    #[inline]
    fn not_sysex(data: &[u8]) -> bool {
        data[0] != 0xF0
    }

    #[inline]
    fn has_status_byte(data: &[u8]) -> bool {
        data[0] & 0b10000000 != 0
    }

    #[inline]
    fn packet_size(data_len: usize) -> usize {
        PACKET_HEADER_SIZE + data_len
    }

    #[inline]
    fn packet_list(&self) -> &PacketListInner {
        &self.storage.deref().inner
    }

    #[inline]
    fn packet_list_is_empty(&self) -> bool {
        self.packet_list().num_packets == 0
    }

    #[inline]
    fn packet_list_mut(&mut self) -> &mut PacketListInner {
        &mut self.storage.deref_mut().inner
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use coremidi_sys::{MIDIPacketList, MIDITimeStamp};
    use std::mem;

    #[test]
    pub fn packet_struct_layout() {
        let expected_align = if super::alignment::NEEDS_ALIGNMENT {
            4
        } else {
            1
        };
        assert_eq!(expected_align, mem::align_of::<Packet>());
        assert_eq!(expected_align, mem::align_of::<PacketList>());

        let dummy_packet: Packet = unsafe { mem::zeroed() };
        let ptr = &dummy_packet as *const _ as *const u8;
        assert_eq!(
            PACKET_HEADER_SIZE,
            dummy_packet.inner.data.as_ptr() as usize - ptr as usize
        );

        let dummy_packet_list: PacketList = unsafe { mem::zeroed() };
        let ptr = &dummy_packet_list as *const _ as *const u8;
        assert_eq!(
            PACKET_LIST_HEADER_SIZE,
            std::ptr::addr_of!(dummy_packet_list.inner.data) as usize - ptr as usize
        );
    }

    #[test]
    pub fn single_packet_alloc_inline() {
        let packet_buf = PacketBuffer::new(42, &[0x90u8, 0x40, 0x7f]);
        if let PacketBufferStorage::External(_) = packet_buf.storage {
            panic!("A single 3-byte message must not be allocated externally")
        }
    }

    #[test]
    fn packet_buffer_deref() {
        let packet_buf = PacketBuffer::new(42, &[0x90u8, 0x40, 0x7f]);
        let packet_list: &PacketList = &packet_buf;
        assert_eq!(
            unsafe { packet_list.as_ptr() as *const MIDIPacketList },
            packet_buf.storage.get_slice().as_ptr() as *const _ as *const MIDIPacketList
        );
    }

    #[test]
    fn packet_list_length() {
        let mut packet_buf = PacketBuffer::new(42, &[0x90u8, 0x40, 0x7f]);
        packet_buf.push_data(43, &[0x91u8, 0x40, 0x7f]);
        packet_buf.push_data(44, &[0x80u8, 0x40, 0x7f]);
        packet_buf.push_data(45, &[0x81u8, 0x40, 0x7f]);
        assert_eq!(packet_buf.len(), 4);
    }

    #[test]
    fn packet_buffer_empty_with_capacity() {
        let packet_buf = PacketBuffer::with_capacity(128);
        assert_eq!(packet_buf.capacity(), 128);
        assert_eq!(packet_buf.len(), 0);
    }

    #[test]
    fn packet_buffer_with_capacity_zero() {
        let packet_buf = PacketBuffer::with_capacity(0);
        assert_eq!(packet_buf.capacity(), INLINE_PACKET_BUFFER_SIZE);
        assert_eq!(packet_buf.len(), 0);
    }

    #[test]
    fn packet_buffer_with_capacity() {
        let mut packet_buf = PacketBuffer::with_capacity(128);
        packet_buf.push_data(43, &[0x91u8, 0x40, 0x7f]);
        packet_buf.push_data(44, &[0x80u8, 0x40, 0x7f]);
        packet_buf.push_data(45, &[0x81u8, 0x40, 0x7f]);
        assert_eq!(packet_buf.capacity(), 128);
        assert_eq!(packet_buf.len(), 3);
    }

    #[test]
    fn packet_buffer_clear() {
        let mut packet_buf = PacketBuffer::new(42, &[0x90u8, 0x40, 0x7f]);
        packet_buf.push_data(43, &[0x91u8, 0x40, 0x7f]);
        packet_buf.push_data(44, &[0x80u8, 0x40, 0x7f]);
        packet_buf.push_data(45, &[0x81u8, 0x40, 0x7f]);
        assert_eq!(packet_buf.len(), 4);
        packet_buf.clear();
        assert_eq!(packet_buf.len(), 0);
    }

    #[test]
    fn compare_equal_timestamps() {
        // these messages should be merged into a single packet
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (42, vec![0x90, 0x41, 0x7f]),
                (42, vec![0x90, 0x42, 0x7f]),
            ])
        }
    }

    #[test]
    fn compare_unequal_timestamps() {
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (43, vec![0x90, 0x40, 0x7f]),
                (44, vec![0x90, 0x40, 0x7f]),
            ])
        }
    }

    #[test]
    fn compare_sysex() {
        // the sysex must not be merged with the surrounding packets
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (42, vec![0xF0, 0x01, 0x01, 0x01, 0x01, 0x01, 0xF7]), // sysex
                (42, vec![0x90, 0x41, 0x7f]),
            ])
        }
    }

    #[test]
    fn compare_sysex_split() {
        // the sysex must not be merged with the surrounding packets
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (42, vec![0xF0, 0x01, 0x01, 0x01, 0x01]), // sysex part 1
                (42, vec![0x01, 0xF7]),                   // sysex part 2
                (42, vec![0x90, 0x41, 0x7f]),
            ])
        }
    }

    #[test]
    fn compare_sysex_split2() {
        // the sysex must not be merged with the surrounding packets
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (42, vec![0xF0, 0x01, 0x01, 0x01, 0x01]), // sysex part 1
                (42, vec![0x01, 0x01, 0x01]),             // sysex part 2
                (42, vec![0x01, 0xF7]),                   // sysex part 3
                (42, vec![0x90, 0x41, 0x7f]),
            ])
        }
    }

    #[test]
    fn compare_sysex_malformed() {
        // the sysex must not be merged with the surrounding packets
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (42, vec![0xF0, 0x01, 0x01, 0x01, 0x01]), // sysex part 1
                (42, vec![0x01, 0x01, 0x01]),             // sysex part 2
                //(42, vec![0x01, 0xF7]), // sysex part 3 (missing)
                (42, vec![0x90, 0x41, 0x7f]),
            ])
        }
    }

    #[test]
    fn compare_sysex_long() {
        let mut sysex = vec![0xF0];
        sysex.resize(301, 0x01);
        sysex.push(0xF7);
        unsafe {
            compare_packet_list(vec![
                (42, vec![0x90, 0x40, 0x7f]),
                (43, vec![0x90, 0x41, 0x7f]),
                (43, sysex),
            ])
        }
    }

    /// Compares the results of building a PacketList using our PacketBuffer API
    /// and the native API (MIDIPacketListAdd, etc).
    unsafe fn compare_packet_list(packets: Vec<(MIDITimeStamp, Vec<u8>)>) {
        use coremidi_sys::{MIDIPacketListAdd, MIDIPacketListInit};

        // allocate a buffer on the stack for building the list using native methods
        const BUFFER_SIZE: usize = 65536; // maximum allowed size
        let buffer: &mut [u8] = &mut [0; BUFFER_SIZE];
        let pkt_list_ptr = buffer.as_mut_ptr() as *mut MIDIPacketList;

        // build the list
        let mut pkt_ptr = MIDIPacketListInit(pkt_list_ptr);
        for pkt in &packets {
            pkt_ptr = MIDIPacketListAdd(
                pkt_list_ptr,
                BUFFER_SIZE as u64,
                pkt_ptr,
                pkt.0,
                pkt.1.len() as u64,
                pkt.1.as_ptr(),
            );
            assert!(!pkt_ptr.is_null());
        }
        let list_native = &*(pkt_list_ptr as *const _ as *const PacketList);

        // build the PacketBuffer, containing the same packets
        let mut packet_buf = PacketBuffer::new(packets[0].0, &packets[0].1);
        for pkt in &packets[1..] {
            packet_buf.push_data(pkt.0, &pkt.1);
        }

        // print buffer contents for debugging purposes
        let packet_buf_slice = packet_buf.storage.get_slice();
        println!(
            "\nbuffer: {:?}",
            packet_buf_slice
                .iter()
                .map(|b| format!("{:02X}", b))
                .collect::<Vec<String>>()
                .join(" ")
        );
        println!(
            "\nnative: {:?}",
            buffer[0..packet_buf_slice.len()]
                .iter()
                .map(|b| format!("{:02X}", b))
                .collect::<Vec<String>>()
                .join(" ")
        );

        let list: &PacketList = &packet_buf;

        // check if the contents match
        assert_eq!(
            list_native.len(),
            list.len(),
            "PacketList lengths must match"
        );
        for (n, p) in list_native.iter().zip(list.iter()) {
            assert_eq!(n.data(), p.data());
        }
    }
}

[ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge