use byteorder::{BE, ReadBytesExt, WriteBytesExt};
use std::io::{Cursor, Seek};
use strum::FromRepr;
use crate::ascii_str::ArrayList;
use crate::bit_io::{BitRead, BitWrite, BitWriter};
use crate::bits::Bitset;
use crate::{Decode, Encode, metadata};
use coded_number::{CodedNumber, CodedNumberError};
use crate::num::{U15, U20};
pub mod coded_number;
pub mod subframe;
pub const FRAME_SYNC_CODE: U15 = U15!(0b111_1111_1111_1100);
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("IO: {0}")]
Io(#[from] std::io::Error),
#[error("subframe: {0}")]
Subframe(#[from] subframe::Error),
#[error("A reserved block size was used")]
ReservedBlockSize,
#[error("A reserved bit depth was used")]
ReservedBitDepth,
#[error("A forbidden sample rate was used")]
ForbiddenSampleRate,
#[error("Coded number: {0}")]
CodedNumber(#[from] CodedNumberError),
#[error("The frame sync code was invalid")]
InvalidFrameSync,
#[error("A CRC check failed")]
FailedCrc,
#[error("A reserved bit was used")]
ReservedBit,
#[error("Reserved channel bits were used")]
ReservedChannelBits,
#[error(
"The last subframe is expected to be padded to a byte boundary, but there was non-zero data leftover"
)]
LeftoverData,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr)]
#[repr(u8)]
pub enum BlockingStrategy {
Fixed = 0,
Variable = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockSize {
B192,
B576,
B1152,
B2304,
B4608,
Uncommon8(u8),
Uncommon16(u16),
B256,
B512,
B1024,
B2048,
B4096,
B8192,
B16384,
B32768,
}
impl BlockSize {
pub const fn samples(self) -> usize {
use BlockSize::*;
match self {
B192 => 192,
B576 => 576,
B1152 => 1152,
B2304 => 2304,
B4608 => 4608,
Uncommon8(sz) => sz as usize + 1,
Uncommon16(sz) => sz as usize + 1,
B256 => 256,
B512 => 512,
B1024 => 1024,
B2048 => 2048,
B4096 => 4096,
B8192 => 8192,
B16384 => 16384,
B32768 => 32768,
}
}
}
impl Decode for BlockSize {
type Error = Error;
#[inline]
fn decode<R: BitRead + Seek>(reader: &mut R, _opt: ()) -> Result<Self, Self::Error> {
use BlockSize::*;
let n = reader.read_bits(4)? as u8;
Ok(match n {
0b0000 => return Err(Error::ReservedBlockSize),
0b0001 => B192,
v @ 0b0010..=0b0101 => match 144u16 * 2u16.pow(u32::from(v)) {
576 => B576,
1152 => B1152,
2304 => B2304,
4608 => B4608,
_ => unreachable!("`v` is gaurenteed to be in the range 0b0010..=0b0101"),
},
0b0110 => Uncommon8(0),
0b0111 => Uncommon16(0),
v @ 0b1000..=0b1111 => match 2u16.pow(u32::from(v)) {
256 => B256,
512 => B512,
1024 => B1024,
2048 => B2048,
4096 => B4096,
8192 => B8192,
16_384 => B16384,
32_768 => B32768,
_ => unreachable!(
"Four bits can not encode a power of two greater than the enumerated"
),
},
16..=u8::MAX => unreachable!("A four bit number can't encode the 16..=u8::MAX range"),
})
}
}
impl Encode for BlockSize {
type Error = Error;
#[inline]
fn encode<W: BitWrite>(&self, writer: &mut W, _opt: ()) -> Result<(), Self::Error> {
use BlockSize::*;
let n = match *self {
B192 => 0b0001,
B576 => (576_u16 / 144).ilog2(),
B1152 => (1152_u16 / 144).ilog2(),
B2304 => (2304_u16 / 144).ilog2(),
B4608 => (4608_u16 / 144).ilog2(),
Uncommon8(_) => 0b0110,
Uncommon16(_) => 0b0111,
B256 => 256_u16.ilog2(),
B512 => 512_u16.ilog2(),
B1024 => 1024_u16.ilog2(),
B2048 => 2048_u16.ilog2(),
B4096 => 4096_u16.ilog2(),
B8192 => 8192_u16.ilog2(),
B16384 => 16_384_u16.ilog2(),
B32768 => 32_768_u16.ilog2(),
};
writer.write_bits(n.into(), 4)?;
Ok(())
}
}
#[allow(clippy::enum_variant_names)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum SampleRate {
Streaminfo = 0b0000,
KHz88_2 = 0b0001,
KHz176_4 = 0b0010,
KHz192 = 0b0011,
KHz8 = 0b0100,
KHz16 = 0b0101,
KHz22_05 = 0b0110,
KHz24 = 0b0111,
KHz32 = 0b1000,
KHz44_1 = 0b1001,
KHz48 = 0b1010,
KHz96 = 0b1011,
UncommonKHz(u8) = 0b1100,
UncommonHz(u16) = 0b1101,
UncommonHzDiv(U20) = 0b1110,
}
impl SampleRate {
pub const fn rate(self, streaminfo: &metadata::Streaminfo) -> U20 {
use SampleRate::*;
U20::new(match self {
Streaminfo => return streaminfo.sample_rate,
KHz88_2 => 88_200,
KHz176_4 => 176_400,
KHz192 => 192_000,
KHz8 => 8_000,
KHz16 => 16_000,
KHz22_05 => 22_050,
KHz24 => 24_000,
KHz32 => 32_000,
KHz44_1 => 44_100,
KHz48 => 48_000,
KHz96 => 96_000,
UncommonKHz(khz) => khz as u32 * 1000,
UncommonHz(hz) => hz as u32,
UncommonHzDiv(hz) => hz.inner() * 10,
})
.unwrap()
}
}
impl Decode for SampleRate {
type Error = Error;
#[inline]
fn decode<R: BitRead + Seek>(reader: &mut R, _opt: ()) -> Result<Self, Self::Error> {
use SampleRate::*;
let n = reader.read_bits(4)? as u8;
Ok(match n {
0b0000 => Streaminfo,
0b0001 => KHz88_2,
0b0010 => KHz176_4,
0b0011 => KHz192,
0b0100 => KHz8,
0b0101 => KHz16,
0b0110 => KHz22_05,
0b0111 => KHz24,
0b1000 => KHz32,
0b1001 => KHz44_1,
0b1010 => KHz48,
0b1011 => KHz96,
0b1100 => UncommonKHz(0),
0b1101 => UncommonHz(0),
0b1110 => UncommonHzDiv(U20!(0)),
0b1111 => return Err(Error::ForbiddenSampleRate),
16..=u8::MAX => unreachable!("A four bit number can't encode the 16..=u8::MAX range"),
})
}
}
impl Encode for SampleRate {
type Error = Error;
#[inline]
fn encode<W: BitWrite>(&self, writer: &mut W, _opt: ()) -> Result<(), Self::Error> {
use SampleRate::*;
let n = match *self {
Streaminfo => 0b0000,
KHz88_2 => 0b0001,
KHz176_4 => 0b0010,
KHz192 => 0b0011,
KHz8 => 0b0100,
KHz16 => 0b0101,
KHz22_05 => 0b0110,
KHz24 => 0b0111,
KHz32 => 0b1000,
KHz44_1 => 0b1001,
KHz48 => 0b1010,
KHz96 => 0b1011,
UncommonKHz(_) => 0b1100,
UncommonHz(_) => 0b1101,
UncommonHzDiv(_) => 0b1110,
};
writer.write_bits(n, 4)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Channels {
One = 0b0000,
Two = 0b0001,
Three,
Four,
Five,
Six,
Seven,
Eight,
LeftSideStereo,
SideRightStereo,
MidSideStereo = 0b1010,
}
impl Channels {
pub const fn side_flags(self) -> [bool; 8] {
match self {
Channels::SideRightStereo => [true, false, false, false, false, false, false, false],
Channels::LeftSideStereo | Channels::MidSideStereo => {
[false, true, false, false, false, false, false, false]
}
_ => [false; 8],
}
}
pub const fn count(self) -> usize {
match self {
Channels::One => 1,
Channels::Two
| Channels::LeftSideStereo
| Channels::SideRightStereo
| Channels::MidSideStereo => 2,
Channels::Three => 3,
Channels::Four => 4,
Channels::Five => 5,
Channels::Six => 6,
Channels::Seven => 7,
Channels::Eight => 8,
}
}
}
impl Decode for Channels {
type Error = Error;
#[inline]
fn decode<R: BitRead + Seek>(reader: &mut R, _opt: ()) -> Result<Self, Self::Error> {
use Channels::*;
let n = reader.read_bits(4)? as u8;
Ok(match n {
0b0000 => One,
0b0001 => Two,
0b0010 => Three,
0b0011 => Four,
0b0100 => Five,
0b0101 => Six,
0b0110 => Seven,
0b0111 => Eight,
0b1000 => LeftSideStereo,
0b1001 => SideRightStereo,
0b1010 => MidSideStereo,
0b1011..=0b1111 => return Err(Error::ReservedChannelBits),
16..=u8::MAX => panic!("A four bit number can't encode the 16..=u8::MAX range"),
})
}
}
impl Encode for Channels {
type Error = Error;
#[inline]
fn encode<W: BitWrite>(&self, writer: &mut W, _opt: ()) -> Result<(), Self::Error> {
use Channels::*;
let n = match self {
One => 0b0000,
Two => 0b0001,
Three => 0b0010,
Four => 0b0011,
Five => 0b0100,
Six => 0b0101,
Seven => 0b0110,
Eight => 0b0111,
LeftSideStereo => 0b1000,
SideRightStereo => 0b1001,
MidSideStereo => 0b1010,
};
writer.write_bits(n, 4)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BitDepth {
Streaminfo = 0b0000,
Eight,
Twelve,
Sixteen,
Twenty,
TwentyFour,
ThirtyTwo,
}
impl Decode for BitDepth {
type Error = Error;
#[inline]
fn decode<R: BitRead + Seek>(reader: &mut R, _opt: ()) -> Result<Self, Self::Error> {
let n = reader.read_bits(3)? as u8;
Ok(match n {
0b000 => Self::Streaminfo,
0b001 => Self::Eight,
0b010 => Self::Twelve,
0b011 => return Err(Error::ReservedBitDepth),
0b100 => Self::Sixteen,
0b101 => Self::Twenty,
0b110 => Self::TwentyFour,
0b111 => Self::ThirtyTwo,
0b1000..=u8::MAX => {
panic!("A three bit number can't encode the 0b1000..=u8::MAX range")
}
})
}
}
impl Encode for BitDepth {
type Error = Error;
#[inline]
fn encode<W: BitWrite>(&self, writer: &mut W, _opt: ()) -> Result<(), Self::Error> {
let n = match self {
Self::Streaminfo => 0b000,
Self::Eight => 0b001,
Self::Twelve => 0b010,
Self::Sixteen => 0b100,
Self::Twenty => 0b101,
Self::TwentyFour => 0b110,
Self::ThirtyTwo => 0b111,
};
writer.write_bits(n, 3)?;
Ok(())
}
}
pub fn verify_header_crc(bytes: &[u8]) -> u8 {
const POLYNOMIAL: u8 = 0b0000_0111;
let mut crc: u8 = 0;
for &byte in bytes {
crc ^= byte;
for _ in 0..8 {
let msb = crc.get_bit_msb(0);
crc <<= 1;
if msb {
crc ^= POLYNOMIAL;
}
}
}
crc
}
pub fn verify_footer_crc(bytes: &[u8]) -> u16 {
const POLYNOMIAL: u16 = 0x8005;
let mut crc: u16 = 0;
for &byte in bytes {
crc ^= u16::from(byte) << 8;
for _ in 0..8 {
let msb = crc.get_bit_msb(0);
crc <<= 1;
if msb {
crc ^= POLYNOMIAL;
}
}
}
crc
}
#[derive(Debug, Clone, Copy)]
pub struct Header {
pub blocking_strategy: BlockingStrategy,
pub block_size: BlockSize,
pub sample_rate: SampleRate,
pub channels: Channels,
pub bit_depth: BitDepth,
pub coded_number: CodedNumber,
pub crc: u8,
}
impl Header {
#[inline]
pub fn is_streamable(&self, streaminfo: &metadata::Streaminfo) -> bool {
!matches!(self.sample_rate, SampleRate::Streaminfo)
&& !matches!(self.bit_depth, BitDepth::Streaminfo)
&& self.block_size.samples() <= 16384
&& if self.sample_rate.rate(streaminfo) <= 48_000 {
self.block_size.samples() <= 4608
} else {
true
}
}
#[inline]
pub const fn bit_depth(&self, streaminfo: &crate::metadata::Streaminfo) -> u8 {
use BitDepth::*;
match self.bit_depth {
Streaminfo => streaminfo.bits_per_sample(),
Eight => 8,
Twelve => 12,
Sixteen => 16,
Twenty => 20,
TwentyFour => 24,
ThirtyTwo => 32,
}
}
}
impl Decode<()> for Header {
type Error = Error;
fn decode<R: BitRead + Seek>(reader: &mut R, _opt: ()) -> Result<Self, Self::Error> {
debug_assert!(reader.is_byte_aligned());
let blocking_strategy = match reader.read_u16::<BE>()? {
0xFFF8 => BlockingStrategy::Fixed,
0xFFF9 => BlockingStrategy::Variable,
_ => return Err(Error::InvalidFrameSync),
};
let partial_block_size = BlockSize::decode(reader, ())?;
let partial_sample_rate = SampleRate::decode(reader, ())?;
debug_assert!(reader.is_byte_aligned());
let channels = Channels::decode(reader, ())?;
let bit_depth = BitDepth::decode(reader, ())?;
if reader.read_bit()? {
return Err(Error::ReservedBit);
}
debug_assert!(reader.is_byte_aligned());
let coded_number = CodedNumber::decode(reader, blocking_strategy)?;
debug_assert!(reader.is_byte_aligned());
let block_size = match partial_block_size {
BlockSize::Uncommon8(_) => BlockSize::Uncommon8(reader.read_u8()?),
BlockSize::Uncommon16(_) => BlockSize::Uncommon16(reader.read_u16::<BE>()?),
p => p,
};
let sample_rate = match partial_sample_rate {
SampleRate::UncommonKHz(_) => SampleRate::UncommonKHz(reader.read_u8()?),
SampleRate::UncommonHz(_) => SampleRate::UncommonHz(reader.read_u16::<BE>()?),
SampleRate::UncommonHzDiv(_) => {
let b = reader.read_u16::<BE>()?;
SampleRate::UncommonHzDiv(U20!(u32::from(b) * 10))
}
p => p,
};
let crc = reader.read_u8()?;
Ok(Self {
blocking_strategy,
block_size,
sample_rate,
channels,
bit_depth,
coded_number,
crc,
})
}
}
impl Encode for Header {
type Error = Error;
fn encode<W: BitWrite>(&self, writer: &mut W, _opt: ()) -> Result<(), Self::Error> {
debug_assert!(writer.is_byte_aligned());
let mut array_buf = ArrayList::<u8, 16>::default_with_len(15);
let piss_in_my_mouth = {
let mut buf = BitWriter::new(Cursor::new(&mut *array_buf));
let sync: u16 = match self.blocking_strategy {
BlockingStrategy::Fixed => 0xFFF8,
BlockingStrategy::Variable => 0xFFF9,
};
buf.write_u16::<BE>(sync)?;
self.block_size.encode(&mut buf, ())?;
self.sample_rate.encode(&mut buf, ())?;
self.channels.encode(&mut buf, ())?;
self.bit_depth.encode(&mut buf, ())?;
buf.write_bit(false)?; debug_assert!(buf.is_byte_aligned());
self.coded_number.encode(&mut buf, ())?;
match self.block_size {
BlockSize::Uncommon8(n) => buf.write_u8(n)?,
BlockSize::Uncommon16(n) => buf.write_u16::<BE>(n)?,
_ => {}
}
match self.sample_rate {
SampleRate::UncommonKHz(n) => buf.write_u8(n)?,
SampleRate::UncommonHz(n) => buf.write_u16::<BE>(n)?,
SampleRate::UncommonHzDiv(n) => buf.write_u16::<BE>((n.inner() / 10) as u16)?,
_ => {}
}
buf.into_inner().position() as usize
};
array_buf.set_len(piss_in_my_mouth);
let crc = verify_header_crc(&array_buf);
array_buf.push(crc);
writer.write_all(&array_buf)?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Footer {
pub crc: u16,
}
impl Decode for Footer {
type Error = Error;
#[inline]
fn decode<R: BitRead + Seek>(reader: &mut R, _opt: ()) -> Result<Self, Self::Error> {
if reader.take_leftover().is_some_and(|(bits, _len)| bits != 0) {
return Err(Error::LeftoverData);
}
let crc = reader.read_u16::<BE>()?;
Ok(Self { crc })
}
}
#[derive(Clone)]
pub struct Frame {
pub header: Header,
pub blocks: Vec<subframe::Block>,
}
impl std::fmt::Debug for Frame {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Frame")
.field("header", &self.header)
.field("blocks", &"...")
.finish()
}
}
impl Frame {
pub fn read_streaming<R: BitRead + Seek, CB>(
reader: &mut R,
streaminfo: &metadata::Streaminfo,
mut callback: CB,
) -> Result<(Header, Footer), Error>
where
CB: FnMut(&Header, Result<subframe::Block, subframe::Error>) -> Result<(), subframe::Error>,
{
let starting_pos = reader.stream_position()?;
let header = {
let header = Header::decode(reader, ())?;
let current_pos = reader.stream_position()?;
let size = current_pos - starting_pos;
let mut crc_buf = vec![0u8; size as usize];
reader.seek_relative(-(size as i64))?;
reader.read_exact(&mut crc_buf)?;
debug_assert_eq!(current_pos, reader.stream_position()?);
if verify_header_crc(&crc_buf) != 0 {
return Err(Error::FailedCrc);
}
header
};
for block in subframe::BlockIter::new(
reader,
header.block_size.samples(),
header.bit_depth(streaminfo),
&header.channels.side_flags()[..header.channels.count()],
) {
callback(&header, block)?;
}
let footer = {
let footer = Footer::decode(reader, ())?;
let current_pos = reader.stream_position()?;
let size = current_pos - starting_pos;
let mut crc_buf = vec![0u8; size as usize];
reader.seek_relative(-(size as i64))?;
reader.read_exact(&mut crc_buf)?;
debug_assert_eq!(current_pos, reader.stream_position()?);
if verify_footer_crc(&crc_buf) != 0 {
return Err(Error::FailedCrc);
}
footer
};
Ok((header, footer))
}
pub fn encode_streaming<'a, W: BitWrite, CB>(
writer: &mut W,
header: &Header,
streaminfo: &metadata::Streaminfo,
mut callback: CB,
) -> Result<(), Error>
where
CB: FnMut() -> Result<Option<&'a subframe::Block>, Error>, {
let mut buf = BitWriter::new(Cursor::new(Vec::new()));
header.encode(&mut buf, ())?;
let mut channel_idx = 0;
while let Some(block) = callback()? {
let is_side_channel = header.channels.side_flags()[channel_idx];
block.encode(
&mut buf,
subframe::BlockEncodingParameters {
header: subframe::Header {
kind: subframe::Type::Verbatim,
wasted_bits: 0,
},
frame_bit_depth: header.bit_depth(streaminfo),
is_side_channel,
},
)?;
channel_idx += 1;
}
buf.flush_bits()?;
let buf = buf.into_inner().into_inner();
let crc = verify_footer_crc(&buf);
writer.write_all(&buf)?;
writer.write_u16::<BE>(crc)?;
Ok(())
}
}
impl Encode<&metadata::Streaminfo> for Frame {
type Error = Error;
fn encode<W: BitWrite>(
&self,
writer: &mut W,
streaminfo: &metadata::Streaminfo,
) -> Result<(), Self::Error> {
let mut i = 0;
Self::encode_streaming(writer, &self.header, streaminfo, || {
i += 1;
Ok(self.blocks.get(i - 1))
})?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bit_io::BitReader;
use std::io::Cursor;
fn create_frame_buffer(mut data: Vec<u8>) -> Vec<u8> {
let crc = verify_header_crc(&data);
data.push(crc);
data
}
fn raw_fixed(byte2: u8, byte3: u8) -> Vec<u8> {
vec![0xFF, 0xF8, byte2, byte3, 0x00]
}
fn raw_variable(byte2: u8, byte3: u8) -> Vec<u8> {
vec![0xFF, 0xF9, byte2, byte3, 0x00]
}
const B3_ONE_16BIT: u8 = 0b100 << 1;
const B3_TWO_16BIT: u8 = (0x1 << 4) | (0b100 << 1);
fn decode(buf: Vec<u8>) -> Result<Header, Error> {
Header::decode(&mut BitReader::new(Cursor::new(buf)), ())
}
#[test]
fn crc_empty_is_zero() {
assert_eq!(verify_header_crc(&[]), 0);
}
#[test]
fn crc_self_check_yields_zero() {
let data = &[0xFF, 0xF8, 0x19, 0x18, 0x00u8];
let crc = verify_header_crc(data);
let mut with_crc = data.to_vec();
with_crc.push(crc);
assert_eq!(verify_header_crc(&with_crc), 0);
}
#[test]
fn crc_single_bit_flip_is_detected() {
let data = vec![0xFF, 0xF8, 0x19, 0x18, 0x00];
let c1 = verify_header_crc(&data);
let mut flipped = data.clone();
flipped[2] ^= 0x01;
assert_ne!(c1, verify_header_crc(&flipped));
}
#[test]
fn crc16_empty_is_zero() {
assert_eq!(verify_footer_crc(&[]), 0);
}
#[test]
fn crc16_self_check_yields_zero() {
let data = &[0xFF, 0xF8, 0x19, 0x18, 0x00u8];
let crc = verify_footer_crc(data);
let mut with_crc = data.to_vec();
with_crc.extend_from_slice(&crc.to_be_bytes());
assert_eq!(verify_footer_crc(&with_crc), 0);
}
#[test]
fn crc16_single_bit_flip_detected() {
let data = vec![0xFF, 0xF8, 0x19, 0x18, 0x00];
let c1 = verify_footer_crc(&data);
let mut flipped = data.clone();
flipped[2] ^= 0x01;
assert_ne!(c1, verify_footer_crc(&flipped));
}
#[test]
fn block_size_partial_all_nibbles() {
use BlockSize::*;
let cases: [(u8, BlockSize); 15] = [
(0x1, B192),
(0x2, B576),
(0x3, B1152),
(0x4, B2304),
(0x5, B4608),
(0x6, Uncommon8(0)),
(0x7, Uncommon16(0)),
(0x8, B256),
(0x9, B512),
(0xA, B1024),
(0xB, B2048),
(0xC, B4096),
(0xD, B8192),
(0xE, B16384),
(0xF, B32768),
];
for (n, expected) in cases {
let n = [n << 4; 1];
assert_eq!(
BlockSize::decode(&mut BitReader::new(Cursor::new(n)), ()).unwrap(),
expected,
"nibble {n:#04x}",
n = n[0]
);
}
}
#[test]
fn integration_block_size_non_extension_variants() {
use BlockSize::*;
let cases: [(u8, BlockSize); 13] = [
(0x1, B192),
(0x2, B576),
(0x3, B1152),
(0x4, B2304),
(0x5, B4608),
(0x8, B256),
(0x9, B512),
(0xA, B1024),
(0xB, B2048),
(0xC, B4096),
(0xD, B8192),
(0xE, B16384),
(0xF, B32768),
];
for (nibble, expected) in cases {
let buf = create_frame_buffer(raw_fixed((nibble << 4) | 0x9, B3_ONE_16BIT));
let h = decode(buf).unwrap_or_else(|e| panic!("nibble {nibble:#04x}: {e}"));
assert_eq!(h.block_size, expected, "nibble {nibble:#04x}");
}
}
#[test]
fn integration_block_size_uncommon8_boundaries() {
for value in [u8::MIN, 1, 127, u8::MAX] {
let mut raw = raw_fixed(0x69, B3_ONE_16BIT); raw.push(value);
let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(h.block_size, BlockSize::Uncommon8(value), "value={value}");
}
}
#[test]
fn integration_block_size_uncommon16_boundaries() {
for value in [u16::MIN, 1, 256, u16::MAX] {
let mut raw = raw_fixed(0x79, B3_ONE_16BIT); raw.extend_from_slice(&value.to_be_bytes());
let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(h.block_size, BlockSize::Uncommon16(value), "value={value}");
}
}
#[test]
fn regression_block_size_nibble_0111_is_uncommon16() {
let mut raw = raw_fixed(0x79, B3_ONE_16BIT);
raw.extend_from_slice(&1234u16.to_be_bytes());
let h = decode(create_frame_buffer(raw)).unwrap();
assert!(matches!(h.block_size, BlockSize::Uncommon16(1234)));
}
#[test]
fn integration_sample_rate_uncommon_khz() {
for value in [0u8, 1, 100, u8::MAX] {
let mut raw = raw_fixed(0x1C, B3_ONE_16BIT); raw.push(value);
let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(
h.sample_rate,
SampleRate::UncommonKHz(value),
"value={value}"
);
}
}
#[test]
fn integration_sample_rate_uncommon_hz() {
for value in [0u16, 1, 48000, u16::MAX] {
let mut raw = raw_fixed(0x1D, B3_ONE_16BIT); raw.extend_from_slice(&value.to_be_bytes());
let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(
h.sample_rate,
SampleRate::UncommonHz(value),
"value={value}"
);
}
}
#[test]
fn integration_sample_rate_uncommon_hz_div_multiplied_by_ten() {
for (stored, expected_hz) in [(4410u16, 44100u32), (9600, 96000), (1, 10)] {
let mut raw = raw_fixed(0x1E, B3_ONE_16BIT); raw.extend_from_slice(&stored.to_be_bytes());
let h = decode(create_frame_buffer(raw)).unwrap();
match h.sample_rate {
SampleRate::UncommonHzDiv(v) => {
assert_eq!(v, expected_hz, "stored={stored}")
}
other => panic!("Expected UncommonHzDiv, got {other:?}"),
}
}
}
#[test]
fn integration_channels_all_standard() {
use Channels::*;
let cases: [(u8, Channels); 11] = [
(0x0, One),
(0x1, Two),
(0x2, Three),
(0x3, Four),
(0x4, Five),
(0x5, Six),
(0x6, Seven),
(0x7, Eight),
(0x8, LeftSideStereo),
(0x9, SideRightStereo),
(0xA, MidSideStereo),
];
for (nibble, expected) in cases {
let byte3 = (nibble << 4) | (0b100u8 << 1); let buf = create_frame_buffer(raw_fixed(0x19, byte3));
let h = decode(buf).unwrap_or_else(|e| panic!("nibble {nibble:#04x}: {e}"));
assert_eq!(h.channels, expected, "nibble {nibble:#04x}");
}
}
#[test]
fn integration_fixed_frame_number_zero() {
let buf = create_frame_buffer(raw_fixed(0x19, B3_TWO_16BIT));
let h = decode(buf).unwrap();
assert_eq!(h.blocking_strategy, BlockingStrategy::Fixed);
assert_eq!(h.coded_number, CodedNumber::FrameNumber(U31!(0)));
}
#[test]
fn integration_fixed_frame_number_max() {
let mut raw = vec![0xFF, 0xF8, 0x19, B3_TWO_16BIT];
raw.extend_from_slice(&[0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF]);
let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(h.coded_number, CodedNumber::FrameNumber(U31!(0x7FFF_FFFF)));
}
#[test]
fn integration_variable_sample_number_zero() {
let buf = create_frame_buffer(raw_variable(0x19, B3_TWO_16BIT));
let h = decode(buf).unwrap();
assert_eq!(h.blocking_strategy, BlockingStrategy::Variable);
assert_eq!(h.coded_number, CodedNumber::SampleNumber(U36!(0)));
}
#[test]
fn integration_variable_sample_number_max() {
let mut raw = vec![0xFF, 0xF9, 0x19, B3_TWO_16BIT];
raw.extend_from_slice(&[0xFE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF]); let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(
h.coded_number,
CodedNumber::SampleNumber(U36!(0xF_FFFF_FFFF))
);
}
#[test]
fn integration_both_extensions_uncommon16_and_uncommon_hz_div() {
let mut raw = vec![0xFF, 0xF8, 0x7E, B3_ONE_16BIT, 0x00];
raw.extend_from_slice(&1000u16.to_be_bytes()); raw.extend_from_slice(&4800u16.to_be_bytes()); let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(h.block_size, BlockSize::Uncommon16(1000));
match h.sample_rate {
SampleRate::UncommonHzDiv(v) => assert_eq!(v, 48000),
other => panic!("Expected UncommonHzDiv, got {other:?}"),
}
}
#[test]
fn integration_both_extensions_uncommon8_and_uncommon_hz() {
let mut raw = vec![0xFF, 0xF8, 0x6D, B3_ONE_16BIT, 0x00];
raw.push(200u8); raw.extend_from_slice(&22_050u16.to_be_bytes()); let h = decode(create_frame_buffer(raw)).unwrap();
assert_eq!(h.block_size, BlockSize::Uncommon8(200));
assert_eq!(h.sample_rate, SampleRate::UncommonHz(22050));
}
#[test]
fn integration_max_length_header_all_extensions() {
let mut raw = vec![
0xFF, 0xF9, 0x7E, 0x7E, ];
raw.extend_from_slice(&[0xFE, 0x8F, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF]);
raw.extend_from_slice(&65_535u16.to_be_bytes()); raw.extend_from_slice(&44_100u16.to_be_bytes()); let h = decode(create_frame_buffer(raw)).unwrap();
assert!(matches!(h.block_size, BlockSize::Uncommon16(65_535)));
match h.sample_rate {
SampleRate::UncommonHzDiv(v) => assert_eq!(v, 441_000),
other => panic!("Expected UncommonHzDiv, got {other:?}"),
}
assert_eq!(h.channels, Channels::Eight);
assert_eq!(h.bit_depth, BitDepth::ThirtyTwo);
}
#[test]
fn error_invalid_sync_code() {
assert!(matches!(
decode(vec![0xEE, 0xEE, 0x00, 0x00, 0x00, 0x00]),
Err(Error::InvalidFrameSync)
));
}
#[test]
fn error_sync_adjacent_values_rejected() {
for sync in [0xFFF7u16, 0xFFFA] {
let buf = [&sync.to_be_bytes()[..], &[0x19, 0x18, 0x00, 0x00]].concat();
assert!(
matches!(decode(buf), Err(Error::InvalidFrameSync)),
"sync={sync:#06x}"
);
}
}
#[test]
fn error_reserved_bit_set() {
let raw = vec![0xFF, 0xF8, 0x19, 0x19, 0x00];
assert!(matches!(
decode(create_frame_buffer(raw)),
Err(Error::ReservedBit)
));
}
#[test]
fn error_crc_fully_inverted() {
let mut buf = create_frame_buffer(vec![0xFF, 0xF8, 0x19, 0x18, 0x00]);
*buf.last_mut().unwrap() ^= 0xFF;
assert_ne!(verify_header_crc(&buf), 0);
}
#[test]
fn error_crc_single_bit_corrupted() {
let mut buf = create_frame_buffer(vec![0xFF, 0xF8, 0x19, 0x18, 0x00]);
*buf.last_mut().unwrap() ^= 0x01;
assert_ne!(verify_header_crc(&buf), 0);
}
#[test]
fn error_crc_body_byte_corrupted() {
let mut buf = create_frame_buffer(vec![0xFF, 0xF8, 0x19, 0x18, 0x00]);
buf[2] ^= 0x01;
assert_ne!(verify_header_crc(&buf), 0);
}
#[test]
fn error_coded_number_overlong_propagates() {
let raw = vec![0xFF, 0xF8, 0x19, 0x18, 0xC1, 0xBF];
assert!(matches!(
decode(create_frame_buffer(raw)),
Err(Error::CodedNumber(_))
));
}
#[test]
fn error_coded_number_out_of_range_propagates() {
let mut raw = vec![0xFF, 0xF8, 0x19, 0x18];
raw.extend_from_slice(&[0xFE, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80]);
assert!(matches!(
decode(create_frame_buffer(raw)),
Err(Error::CodedNumber(_))
));
}
#[test]
fn error_truncated_after_sync() {
assert!(matches!(decode(vec![0xFF, 0xF8]), Err(Error::Io(_))));
}
#[test]
fn error_empty_input() {
assert!(matches!(decode(vec![]), Err(Error::Io(_))));
}
}