use coremidi_sys::{
MIDITimeStamp, MIDIPacket, MIDIPacketNext
};
use std::fmt;
use std::slice;
use std::ops::{Deref, DerefMut};
use PacketList;
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]; pub const NEEDS_ALIGNMENT: bool = true;
}
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
pub mod alignment {
pub type Marker = [u8; 0]; pub const NEEDS_ALIGNMENT: bool = false;
}
#[repr(C)]
pub struct Packet {
inner: PacketInner,
_alignment_marker: alignment::Marker
}
#[repr(packed)]
struct PacketInner {
timestamp: MIDITimeStamp,
length: u16,
data: [u8; 0], }
impl Packet {
pub fn timestamp(&self) -> Timestamp {
self.inner.timestamp as Timestamp
}
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)
}
})
}
}
impl PacketList {
pub fn len(&self) -> usize {
self.inner.num_packets as usize
}
pub fn iter<'a>(&'a self) -> PacketListIterator<'a> {
PacketListIterator {
count: self.len(),
packet_ptr: self.inner.data.as_ptr(),
_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 result = write!(f, "PacketList(len={})", self.inner.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; const PACKET_HEADER_SIZE: usize = 8 + 2;
const INLINE_PACKET_BUFFER_SIZE: usize = 28;
enum PacketBufferStorage {
Inline([u32; INLINE_PACKET_BUFFER_SIZE / 4]),
External(Vec<u32>)
}
impl PacketBufferStorage {
#[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, offset: usize, time: MIDITimeStamp, data: &[u8]) {
assert!(data.len() <= MAX_PACKET_DATA_LENGTH, "packet data too long"); if alignment::NEEDS_ALIGNMENT {
debug_assert!(offset & 0b11 == 0);
}
let slice = self.get_slice_mut();
let mut ptr = slice[offset..].as_mut_ptr() as *mut Packet;
(*ptr).inner.timestamp = time;
(*ptr).inner.length = data.len() as u16;
let packet_data_start = offset + PACKET_HEADER_SIZE;
slice[packet_data_start..(packet_data_start + data.len())].copy_from_slice(data);
}
unsafe fn extend_packet(&mut self, offset: usize, data: &[u8]) {
let slice = self.get_slice_mut();
let mut ptr = slice[offset..].as_mut_ptr() as *mut Packet;
let packet_data_start = 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);
}
unsafe fn set_len(&mut self, new_length: usize) {
if new_length < INLINE_PACKET_BUFFER_SIZE { return; }
if new_length < self.get_slice().len() { return; }
let u32_len = ((new_length - 1) / 4) + 1;
let vec: Option<Vec<u32>> = match *self {
PacketBufferStorage::Inline(ref inline) => {
let mut v = Vec::with_capacity(u32_len);
v.extend_from_slice(inline);
v.set_len(u32_len);
Some(v)
},
PacketBufferStorage::External(ref mut vec) => {
let current_len = vec.len();
vec.reserve(u32_len - current_len);
vec.set_len(u32_len);
None
}
};
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 {
#[inline]
fn deref_mut(&mut self) -> &mut PacketList {
unsafe { &mut *(self.get_slice_mut().as_mut_ptr() as *mut PacketList) }
}
}
pub struct PacketBuffer {
storage: PacketBufferStorage,
last_written_pkt_offset: usize
}
impl Deref for PacketBuffer {
type Target = PacketList;
#[inline]
fn deref(&self) -> &PacketList {
self.storage.deref()
}
}
impl PacketBuffer {
pub fn new(time: MIDITimeStamp, data: &[u8]) -> PacketBuffer {
let len = data.len() + PACKET_LIST_HEADER_SIZE + PACKET_HEADER_SIZE;
let mut storage = if len <= INLINE_PACKET_BUFFER_SIZE {
PacketBufferStorage::Inline([0; INLINE_PACKET_BUFFER_SIZE / 4])
} else {
PacketBufferStorage::External( unsafe {
let u32_len = ((len - 1) / 4) + 1;
let mut v = Vec::with_capacity(u32_len);
v.set_len(u32_len);
v
})
};
unsafe {
storage.assign_packet(PACKET_LIST_HEADER_SIZE, time, data);
storage.deref_mut().inner.num_packets = 1;
}
PacketBuffer {
storage: storage,
last_written_pkt_offset: PACKET_LIST_HEADER_SIZE
}
}
pub fn push_data(&mut self, time: MIDITimeStamp, data: &[u8]) -> &mut Self {
let (can_merge, previous_data_len) = self.can_merge_into_previous(time, data);
if can_merge {
unsafe {
self.storage.set_len(self.last_written_pkt_offset + PACKET_HEADER_SIZE + previous_data_len + data.len());
self.storage.extend_packet(self.last_written_pkt_offset, data);
}
} else {
let next_offset = self.get_next_offset();
unsafe {
self.storage.set_len(next_offset + PACKET_HEADER_SIZE + data.len());
self.storage.assign_packet(next_offset, time, data);
self.storage.deref_mut().inner.num_packets += 1;
}
self.last_written_pkt_offset = next_offset;
}
self
}
fn can_merge_into_previous(&self, time: MIDITimeStamp, data: &[u8]) -> (bool, usize) {
let previous_packet = self.last_written_packet();
let previous_data_len = previous_packet.data().len();
let can_merge =
previous_packet.timestamp() == time && data[0] != 0xF0 && data[0] & 0b10000000 != 0 && previous_packet.data()[0] != 0xF0 && previous_packet.data()[0] & 0b10000000 != 0 && previous_data_len + data.len() < MAX_PACKET_DATA_LENGTH; (can_merge, previous_data_len)
}
#[inline]
fn last_written_packet(&self) -> &Packet {
let packets_slice = self.storage.get_slice();
let packet_slot = &packets_slice[self.last_written_pkt_offset..];
unsafe { &*(packet_slot.as_ptr() as *const Packet) }
}
#[inline]
fn get_next_offset(&self) -> usize {
let length = self.last_written_packet().inner.length as usize;
let next_unadjusted = self.last_written_pkt_offset + PACKET_HEADER_SIZE + length;
if alignment::NEEDS_ALIGNMENT {
(next_unadjusted + 3) & !(3usize)
} else {
next_unadjusted
}
}
}
#[cfg(test)]
mod tests {
use std::mem;
use coremidi_sys::{MIDITimeStamp, MIDIPacketList};
use PacketList;
use PacketBuffer;
use Packet;
use super::{PACKET_HEADER_SIZE, PACKET_LIST_HEADER_SIZE, PacketBufferStorage};
#[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, dummy_packet_list.inner.data.as_ptr() 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 {
assert!(false, "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 compare_equal_timestamps() {
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() {
unsafe { compare_packet_list(vec![
(42, vec![0x90, 0x40, 0x7f]),
(42, vec![0xF0, 0x01, 0x01, 0x01, 0x01, 0x01, 0xF7]), (42, vec![0x90, 0x41, 0x7f])
]) }
}
#[test]
fn compare_sysex_split() {
unsafe { compare_packet_list(vec![
(42, vec![0x90, 0x40, 0x7f]),
(42, vec![0xF0, 0x01, 0x01, 0x01, 0x01]), (42, vec![0x01, 0xF7]), (42, vec![0x90, 0x41, 0x7f])
]) }
}
#[test]
fn compare_sysex_split2() {
unsafe { compare_packet_list(vec![
(42, vec![0x90, 0x40, 0x7f]),
(42, vec![0xF0, 0x01, 0x01, 0x01, 0x01]), (42, vec![0x01, 0x01, 0x01]), (42, vec![0x01, 0xF7]), (42, vec![0x90, 0x41, 0x7f])
]) }
}
#[test]
fn compare_sysex_malformed() {
unsafe { compare_packet_list(vec![
(42, vec![0x90, 0x40, 0x7f]),
(42, vec![0xF0, 0x01, 0x01, 0x01, 0x01]), (42, vec![0x01, 0x01, 0x01]), (42, vec![0x90, 0x41, 0x7f])
]) }
}
#[test]
fn compare_sysex_long() {
let mut sysex = vec![0xF0];
for _ in 0..300 {
sysex.push(0x01);
}
sysex.push(0xF7);
unsafe { compare_packet_list(vec![
(42, vec![0x90, 0x40, 0x7f]),
(43, vec![0x90, 0x41, 0x7f]),
(43, sysex)
]) }
}
unsafe fn compare_packet_list(packets: Vec<(MIDITimeStamp, Vec<u8>)>) {
use coremidi_sys::{MIDIPacketList, MIDIPacketListInit, MIDIPacketListAdd};
const BUFFER_SIZE: usize = 65536; let buffer: &mut [u8] = &mut [0; BUFFER_SIZE];
let pkt_list_ptr = buffer.as_mut_ptr() as *mut MIDIPacketList;
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);
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);
}
let packet_buf_slice = packet_buf.storage.get_slice();
println!("\nbuffer: {:?}", packet_buf_slice);
println!("\nnative: {:?}", &buffer[0..packet_buf_slice.len()]);
let list: &PacketList = &packet_buf;
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());
}
}
}