use bytes::Buf;
use super::{Error, Result};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Mapping {
family: u8,
streams: u8,
coupled: u8,
channels: u8,
table: [u8; 255],
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Config<'a> {
pub family: u8,
pub streams: u8,
pub coupled: u8,
pub table: &'a [u8],
}
const VORBIS: [(u8, u8, &[u8]); 8] = [
(1, 0, &[0]),
(1, 1, &[0, 1]),
(2, 1, &[0, 2, 1]),
(2, 2, &[0, 1, 2, 3]),
(3, 2, &[0, 4, 1, 2, 3]),
(4, 2, &[0, 4, 1, 2, 3, 5]),
(4, 3, &[0, 4, 1, 2, 3, 5, 6]),
(5, 3, &[0, 6, 1, 2, 3, 4, 5, 7]),
];
impl Mapping {
pub(crate) fn vorbis(channels: u8) -> Result<Self> {
let (streams, coupled, table) = *channels
.checked_sub(1)
.and_then(|index| VORBIS.get(index as usize))
.ok_or(Error::UnsupportedChannelCount(channels as u32))?;
Self::new(Config {
family: 1,
streams,
coupled,
table,
})
}
pub fn new(config: Config<'_>) -> Result<Self> {
let Config {
family,
streams,
coupled,
table,
} = config;
let max_channels = match family {
0 => return Err(Error::UnsupportedMappingFamily(0)),
1 => 8,
_ => 255,
};
if table.is_empty() || table.len() > max_channels {
return Err(Error::UnsupportedChannelCount(table.len() as u32));
}
let decoded = streams as u32 + coupled as u32;
if streams == 0 || coupled > streams || decoded > 255 {
return Err(Error::InvalidMappingTable);
}
if table.iter().any(|&entry| entry != 255 && entry as u32 >= decoded) {
return Err(Error::InvalidMappingTable);
}
let mut padded = [0u8; 255];
padded[..table.len()].copy_from_slice(table);
Ok(Self {
family,
streams,
coupled,
channels: table.len() as u8,
table: padded,
})
}
pub(super) fn parse<T: Buf>(buf: &mut T, family: u8, channels: u8) -> Result<Self> {
if buf.remaining() < 2 + channels as usize {
return Err(Error::MappingTableTooShort);
}
let streams = buf.get_u8();
let coupled = buf.get_u8();
let mut table = [0u8; 255];
buf.copy_to_slice(&mut table[..channels as usize]);
Self::new(Config {
family,
streams,
coupled,
table: &table[..channels as usize],
})
}
pub fn family(&self) -> u8 {
self.family
}
pub fn streams(&self) -> u8 {
self.streams
}
pub fn coupled(&self) -> u8 {
self.coupled
}
pub fn table(&self) -> &[u8] {
&self.table[..self.channels as usize]
}
}
impl std::fmt::Debug for Mapping {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mapping")
.field("family", &self.family)
.field("streams", &self.streams)
.field("coupled", &self.coupled)
.field("table", &self.table())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_validates_the_table() {
fn new(family: u8, streams: u8, coupled: u8, table: &[u8]) -> Result<Mapping> {
Mapping::new(Config {
family,
streams,
coupled,
table,
})
}
let mapping = new(255, 2, 0, &[0, 1, 255]).unwrap();
assert_eq!(mapping.family(), 255);
assert_eq!((mapping.streams(), mapping.coupled()), (2, 0));
assert_eq!(mapping.table(), &[0, 1, 255]);
assert!(matches!(new(0, 1, 1, &[0, 1]), Err(Error::UnsupportedMappingFamily(0))));
assert!(matches!(new(1, 1, 0, &[]), Err(Error::UnsupportedChannelCount(0))));
assert!(matches!(new(1, 9, 0, &[0; 9]), Err(Error::UnsupportedChannelCount(9))));
for (streams, coupled, table) in [(0, 0, &[0][..]), (1, 2, &[0]), (1, 1, &[2]), (200, 100, &[0])] {
assert!(
matches!(new(1, streams, coupled, table), Err(Error::InvalidMappingTable)),
"{streams} streams, {coupled} coupled, {table:?}"
);
}
}
}