use crate::prelude::*;
pub(crate) trait SplitChecked: Sized {
fn split_checked(&mut self, at: usize) -> Option<Self>;
}
impl<'a> SplitChecked for &'a [u8] {
#[inline]
fn split_checked(&mut self, at: usize) -> Option<&'a [u8]> {
if at > self.len() {
None
} else {
let (extracted, remainder) = self.split_at(at);
*self = remainder;
Some(extracted)
}
}
}
pub(crate) trait IntRead: Sized {
fn read(data: &mut &[u8]) -> StdResult<Self, &'static ErrorKind>;
}
pub(crate) trait IntReadBottom7: Sized {
fn read_u7(data: &mut &[u8]) -> StdResult<Self, &'static ErrorKind>;
}
macro_rules! impl_read_int {
{$( $int:ty ),*} => {
$(
impl IntRead for $int {
#[inline]
fn read(raw: &mut &[u8]) -> StdResult<$int, &'static ErrorKind> {
let bytes = raw.split_checked(mem::size_of::<$int>())
.ok_or(err_invalid!("failed to read the expected integer"))?;
Ok(bytes.iter().fold(0,|mut acc,byte| {
acc=acc.checked_shl(8).unwrap_or(0);
acc|=*byte as $int;
acc
}))
}
}
)*
}
}
impl_read_int! {u8,u16,u32}
macro_rules! int_feature {
{ $name:ident ; $inner:tt : read_u7 } => {
impl IntReadBottom7 for $name {
fn read_u7(raw: &mut &[u8]) -> StdResult<$name, &'static ErrorKind> {
let bytes = raw.split_checked(mem::size_of::<$inner>())
.ok_or(err_invalid!("failed to read the expected integer"))?;
if cfg!(feature = "strict") {
ensure!(bytes.iter().all(|byte| bit_range!(*byte, 7..8)==0), err_malformed!("invalid byte with top bit set"));
}
let raw = bytes.iter().fold(0, |mut acc,byte| {
acc <<= 7;
acc |= bit_range!(*byte, 0..7) as $inner;
acc
});
Ok(if cfg!(feature = "strict") {
Self::try_from(raw).ok_or(err_malformed!(stringify!("expected " $name ", found " $inner)))?
}else{
Self::from(raw)
})
}
}
};
{ $name:ident ; $inner:tt : read } => {
impl IntRead for $name {
#[inline]
fn read(raw: &mut &[u8]) -> StdResult<Self, &'static ErrorKind> {
let raw = $inner::read(raw)?;
if cfg!(feature = "strict") {
Ok(Self::try_from(raw).ok_or(err_malformed!(concat!("expected ", stringify!($name), ", found ", stringify!($inner))))?)
}else{
Ok(Self::from(raw))
}
}
}
};
}
macro_rules! restricted_int {
{$(#[$attr:meta])* $name:ident : $inner:tt => $bits:expr ; $( $feature:tt )* } => {
$(#[$attr])*
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Default)]
#[repr(transparent)]
#[allow(non_camel_case_types)]
pub struct $name($inner);
impl From<$inner> for $name {
#[inline]
fn from(raw: $inner) -> $name {
$name::from_int_lossy(raw)
}
}
impl From<$name> for $inner {
#[inline]
fn from(restricted: $name) -> $inner {restricted.0}
}
impl fmt::Display for $name {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl $name {
const MASK: $inner = (1 << $bits) - 1;
#[inline]
pub const fn max_value() -> $name {
$name (Self::MASK)
}
#[inline]
pub const fn new(raw: $inner) -> $name {
$name (raw & Self::MASK)
}
#[inline]
pub const fn from_int_lossy(raw: $inner) -> $name {
$name (raw & Self::MASK)
}
#[inline]
pub fn try_from(raw: $inner) -> Option<$name> {
if raw <= Self::MASK {
Some($name(raw))
}else{
None
}
}
#[inline]
pub fn as_int(self) -> $inner {
Into::into(self)
}
#[inline]
pub fn slice_try_from_int(raw: &[$inner]) -> Option<&[$name]> {
for &int in raw {
if int > Self::MASK {
return None;
}
}
unsafe {
Some(Self::slice_from_int_unchecked(raw))
}
}
#[inline]
pub fn slice_from_int(raw: &[$inner]) -> &[$name] {
let first_oob = raw
.iter()
.position(|&b| b > Self::MASK)
.unwrap_or(raw.len());
unsafe {
Self::slice_from_int_unchecked(&raw[..first_oob])
}
}
#[inline]
pub unsafe fn slice_from_int_unchecked(raw: &[$inner]) -> &[$name] {
&*( raw as *const [$inner] as *const [$name] )
}
#[inline]
pub fn slice_try_from_int_mut(raw: &mut [$inner]) -> Option<&mut [$name]> {
for &int in raw.iter() {
if int > Self::MASK {
return None;
}
}
unsafe {
Some(Self::slice_from_int_unchecked_mut(raw))
}
}
#[inline]
pub fn slice_from_int_mut(raw: &mut [$inner]) -> &mut [$name] {
let first_oob = raw
.iter()
.position(|&b| b > Self::MASK)
.unwrap_or(raw.len());
unsafe {
Self::slice_from_int_unchecked_mut(&mut raw[..first_oob])
}
}
#[inline]
pub unsafe fn slice_from_int_unchecked_mut(raw: &mut [$inner]) -> &mut [$name] {
&mut *( raw as *mut [$inner] as *mut [$name] )
}
#[inline]
pub fn slice_as_int(slice: &[$name]) -> &[$inner] {
unsafe { &*(slice as *const [$name] as *const [$inner]) }
}
#[allow(dead_code)]
#[inline]
pub(crate) fn check_int(raw: $inner) -> StdResult<$name, &'static ErrorKind> {
Self::try_from(raw).ok_or_else(
|| err_invalid!("invalid integer with top bits set")
)
}
}
$( int_feature!{$name ; $inner : $feature} )*
};
}
restricted_int! {u15: u16 => 15; read}
restricted_int! {u14: u16 => 14; read read_u7}
restricted_int! {u7: u8 => 7; read}
restricted_int! {u4: u8 => 4; read}
restricted_int! {u2: u8 => 2; read}
restricted_int! {u24: u32 => 24;}
impl IntRead for u24 {
fn read(raw: &mut &[u8]) -> StdResult<u24, &'static ErrorKind> {
let bytes = raw
.split_checked(3)
.ok_or(err_invalid!("failed to read u24 bytes"))?;
Ok(u24::from(bytes.iter().fold(0, |mut acc, byte| {
acc <<= 8;
acc |= *byte as u32;
acc
})))
}
}
restricted_int! {
u28: u32 => 28;
}
impl IntReadBottom7 for u28 {
fn read_u7(raw: &mut &[u8]) -> StdResult<u28, &'static ErrorKind> {
let mut int: u32 = 0;
for _ in 0..4 {
let byte = match raw.split_checked(1) {
Some(slice) => slice[0],
None => {
if cfg!(feature = "strict") {
bail!(err_malformed!("unexpected eof while reading varlen int"))
} else {
break;
}
}
};
int <<= 7;
int |= bit_range!(byte, 0..7) as u32;
if bit_range!(byte, 7..8) == 0 {
return Ok(u28::from(int));
}
}
if cfg!(feature = "strict") {
Err(err_malformed!("varlen integer larger than 4 bytes"))
} else {
Ok(u28::from(int))
}
}
}
impl u28 {
pub(crate) fn write_varlen<W: Write>(&self, out: &mut W) -> WriteResult<W> {
let int = self.as_int();
let mut skipping = true;
for i in (0..4).rev() {
let byte = ((int >> (i * 7)) & 0x7F) as u8;
if skipping && byte == 0 && i != 0 {
} else {
skipping = false;
let byte = if i == 0 {
byte
} else {
byte | 0x80
};
out.write(&[byte])?;
}
}
Ok(())
}
}
pub(crate) fn read_varlen_slice<'a>(raw: &mut &'a [u8]) -> Result<&'a [u8]> {
let len = u28::read_u7(raw)
.context(err_invalid!("failed to read varlen slice length"))?
.as_int();
Ok(match raw.split_checked(len as usize) {
Some(slice) => slice,
None => {
if cfg!(feature = "strict") {
bail!(err_malformed!("incomplete varlen slice"))
} else {
mem::replace(raw, &[])
}
}
})
}
pub(crate) fn write_varlen_slice<W: Write>(slice: &[u8], out: &mut W) -> WriteResult<W> {
let len = u32::try_from(slice.len())
.ok()
.and_then(u28::try_from)
.ok_or_else(|| W::invalid_input("varlen slice exceeds 28 bits"))?;
len.write_varlen(out)?;
out.write(slice)?;
Ok(())
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum Format {
SingleTrack,
Parallel,
Sequential,
}
impl Format {
pub(crate) fn read(raw: &mut &[u8]) -> Result<Format> {
let format = u16::read(raw)?;
Ok(match format {
0 => Format::SingleTrack,
1 => Format::Parallel,
2 => Format::Sequential,
_ => bail!(err_invalid!("invalid smf format")),
})
}
pub(crate) fn encode(&self) -> [u8; 2] {
let code: u16 = match self {
Format::SingleTrack => 0,
Format::Parallel => 1,
Format::Sequential => 2,
};
code.to_be_bytes()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum Timing {
Metrical(u15),
Timecode(Fps, u8),
}
impl Timing {
pub(crate) fn read(raw: &mut &[u8]) -> Result<Timing> {
let raw =
u16::read(raw).context(err_invalid!("unexpected eof when reading midi timing"))?;
if bit_range!(raw, 15..16) != 0 {
let fps = -(bit_range!(raw, 8..16) as i8);
let subframe = bit_range!(raw, 0..8) as u8;
Ok(Timing::Timecode(
Fps::from_int(fps as u8).ok_or(err_invalid!("invalid smpte fps"))?,
subframe,
))
} else {
Ok(Timing::Metrical(u15::from(raw)))
}
}
pub(crate) fn encode(&self) -> [u8; 2] {
match self {
Timing::Metrical(ticksperbeat) => ticksperbeat.as_int().to_be_bytes(),
Timing::Timecode(framespersec, ticksperframe) => {
[(-(framespersec.as_int() as i8)) as u8, *ticksperframe]
}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct SmpteTime {
hour: u8,
minute: u8,
second: u8,
frame: u8,
subframe: u8,
fps: Fps,
}
impl SmpteTime {
#[inline]
pub fn new(
hour: u8,
minute: u8,
second: u8,
frame: u8,
subframe: u8,
fps: Fps,
) -> Option<SmpteTime> {
macro_rules! check {
($cond:expr) => {{
if !{ $cond } {
return None;
}
}};
}
check!(hour < 24);
check!(minute < 60);
check!(second < 60);
check!(frame < fps.as_int());
check!(subframe < 100);
Some(SmpteTime {
hour,
minute,
second,
frame,
subframe,
fps,
})
}
#[inline]
pub fn hour(&self) -> u8 {
self.hour
}
#[inline]
pub fn minute(&self) -> u8 {
self.minute
}
#[inline]
pub fn second(&self) -> u8 {
self.second
}
#[inline]
pub fn frame(&self) -> u8 {
self.frame
}
#[inline]
pub fn subframe(&self) -> u8 {
self.subframe
}
#[inline]
pub fn fps(&self) -> Fps {
self.fps
}
#[inline]
pub fn second_f32(&self) -> f32 {
self.second as f32
+ ((self.frame as f32 + self.subframe as f32 / 100.0) / self.fps.as_f32())
}
pub(crate) fn read(raw: &mut &[u8]) -> Result<SmpteTime> {
let data = raw
.split_checked(5)
.ok_or(err_invalid!("failed to read smpte time data"))?;
let hour_fps = data[0];
let (hour, fps) = (bit_range!(hour_fps, 0..5), bit_range!(hour_fps, 5..7));
let fps = Fps::from_code(u2::from(fps));
let minute = data[1];
let second = data[2];
let frame = data[3];
let subframe = data[4];
Ok(SmpteTime::new(hour, minute, second, frame, subframe, fps)
.ok_or(err_invalid!("invalid smpte time"))?)
}
pub(crate) fn encode(&self) -> [u8; 5] {
let hour_fps = self.hour() | self.fps().as_code().as_int() << 5;
[
hour_fps,
self.minute(),
self.second(),
self.frame(),
self.subframe(),
]
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum Fps {
Fps24,
Fps25,
Fps29,
Fps30,
}
impl Fps {
pub(crate) fn from_code(code: u2) -> Fps {
match code.as_int() {
0 => Fps::Fps24,
1 => Fps::Fps25,
2 => Fps::Fps29,
3 => Fps::Fps30,
_ => unreachable!(),
}
}
pub(crate) fn as_code(self) -> u2 {
u2::from(match self {
Fps::Fps24 => 0,
Fps::Fps25 => 1,
Fps::Fps29 => 2,
Fps::Fps30 => 3,
})
}
#[inline]
pub fn from_int(raw: u8) -> Option<Fps> {
Some(match raw {
24 => Fps::Fps24,
25 => Fps::Fps25,
29 => Fps::Fps29,
30 => Fps::Fps30,
_ => return None,
})
}
#[inline]
pub fn as_int(self) -> u8 {
match self {
Fps::Fps24 => 24,
Fps::Fps25 => 25,
Fps::Fps29 => 29,
Fps::Fps30 => 30,
}
}
#[inline]
pub fn as_f32(self) -> f32 {
match self.as_int() {
24 => 24.0,
25 => 25.0,
29 => 30.0 / 1.001,
30 => 30.0,
_ => unreachable!(),
}
}
}