use crate::prelude::*;
pub(crate) trait SplitChecked: Sized {
fn split_checked(&mut self, at: usize) -> Option<Self>;
}
impl<'a> SplitChecked for &'a [u8] {
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, ErrorKind>;
}
pub(crate) trait IntReadBottom7: Sized {
fn read_u7(data: &mut &[u8]) -> StdResult<Self, ErrorKind>;
}
macro_rules! impl_read_int {
{$( $int:ty ),*} => {
$(
impl IntRead for $int {
fn read(raw: &mut &[u8]) -> StdResult<$int, 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, ErrorKind> {
let bytes = raw.split_checked(mem::size_of::<$inner>())
.ok_or(err_invalid("failed to read the expected integer"))?;
if !cfg!(feature = "lenient") {
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 = "lenient") {
Self::from(raw)
}else{
Self::try_from(raw).ok_or(err_malformed(stringify!("expected " $name ", found " $inner)))?
})
}
}
};
{ $name:ident ; $inner:tt : read } => {
impl IntRead for $name {
fn read(raw: &mut &[u8]) -> StdResult<Self, ErrorKind> {
let raw = $inner::read(raw)?;
if cfg!(feature = "lenient") {
Ok(Self::from(raw))
}else{
Ok(Self::try_from(raw).ok_or(err_malformed(stringify!("expected " $name ", found " $inner)))?)
}
}
}
};
}
macro_rules! restricted_int {
{$(#[$attr:meta])* $name:ident : $inner:tt => $bits:expr ; $( $feature:tt )* } => {
$(#[$attr])*
#[derive(Copy,Clone,Debug)]
#[allow(non_camel_case_types)]
pub struct $name($inner);
impl From<$inner> for $name {
fn from(raw: $inner) -> Self {
$name (bit_range(raw, 0..$bits))
}
}
impl Into<$inner> for $name {
fn into(self) -> $inner {self.0}
}
impl $name {
pub fn try_from(raw: $inner) -> Option<Self> {
let trunc = bit_range(raw, 0..$bits);
if trunc==raw {
Some($name(trunc))
}else{
None
}
}
pub fn as_int(self)->$inner {Into::into(self)}
}
$( 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, 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, 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 = "lenient") {
break;
} else {
bail!(err_malformed("unexpected eof while reading varlen int"))
}
}
};
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 = "lenient") {
Ok(u28::from(int))
} else {
Err(err_malformed("varlen integer larger than 4 bytes"))
}
}
}
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 = "lenient") {
mem::replace(raw, &[])
} else {
bail!(err_malformed("incomplete varlen slice"))
}
}
})
}
#[derive(Copy, Clone, Debug)]
pub enum Format {
SingleTrack,
Parallel,
Sequential,
}
impl Format {
pub 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")),
})
}
}
#[derive(Copy, Clone, Debug)]
pub enum Timing {
Metrical(u15),
Timecode(Fps, u8),
}
impl Timing {
pub 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)))
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct SmpteTime {
hour: u8,
minute: u8,
second: u8,
frame: u8,
subframe: u8,
fps: Fps,
}
impl SmpteTime {
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,
})
}
pub fn hour(&self) -> u8 {
self.hour
}
pub fn minute(&self) -> u8 {
self.minute
}
pub fn second(&self) -> u8 {
self.second
}
pub fn frame(&self) -> u8 {
self.frame
}
pub fn subframe(&self) -> u8 {
self.subframe
}
pub fn fps(&self) -> Fps {
self.fps
}
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 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"))?)
}
}
#[derive(Copy, Clone, Debug)]
pub enum Fps {
Fps24,
Fps25,
Fps29,
Fps30,
}
impl Fps {
pub fn from_code(code: u2) -> Fps {
match code.as_int() {
0 => Fps::Fps24,
1 => Fps::Fps25,
2 => Fps::Fps29,
3 => Fps::Fps30,
_ => unreachable!(),
}
}
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,
})
}
pub fn as_int(self) -> u8 {
match self {
Fps::Fps24 => 24,
Fps::Fps25 => 25,
Fps::Fps29 => 29,
Fps::Fps30 => 30,
}
}
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!(),
}
}
}