pub trait FromBitStreamWith<'a> {
type Context: 'a;
type Error;
// Required method
fn from_reader<R: BitRead + ?Sized>(
r: &mut R,
context: &Self::Context,
) -> Result<Self, Self::Error>
where Self: Sized;
}
Expand description
Implemented by complex types that require some immutable context to parse themselves from a reader.
§Example
use std::io::{Cursor, Read};
use bitstream_io::{BigEndian, BitRead, BitReader, FromBitStreamWith};
#[derive(Default)]
struct Streaminfo {
minimum_block_size: u16,
maximum_block_size: u16,
minimum_frame_size: u32,
maximum_frame_size: u32,
sample_rate: u32,
channels: u8,
bits_per_sample: u8,
total_samples: u64,
md5: [u8; 16],
}
#[derive(Debug, PartialEq, Eq)]
struct FrameHeader {
variable_block_size: bool,
block_size: u32,
sample_rate: u32,
channel_assignment: u8,
sample_size: u8,
frame_number: u64,
crc8: u8,
}
impl FromBitStreamWith<'_> for FrameHeader {
type Context = Streaminfo;
type Error = FrameHeaderError;
fn from_reader<R: BitRead + ?Sized>(
r: &mut R,
streaminfo: &Streaminfo,
) -> Result<Self, Self::Error> {
if r.read::<u16>(14)? != 0b11111111111110 {
return Err(FrameHeaderError::InvalidSync);
}
if r.read_bit()? != false {
return Err(FrameHeaderError::InvalidReservedBit);
}
let variable_block_size = r.read_bit()?;
let block_size_bits = r.read::<u8>(4)?;
let sample_rate_bits = r.read::<u8>(4)?;
let channel_assignment = r.read::<u8>(4)?;
let sample_size = match r.read::<u8>(3)? {
0b000 => streaminfo.bits_per_sample,
0b001 => 8,
0b010 => 12,
0b011 => return Err(FrameHeaderError::InvalidSampleSize),
0b100 => 16,
0b101 => 20,
0b110 => 24,
0b111 => 32,
_ => unreachable!(),
};
if r.read_bit()? != false {
return Err(FrameHeaderError::InvalidReservedBit);
}
let frame_number = read_utf8(r)?;
Ok(FrameHeader {
variable_block_size,
block_size: match block_size_bits {
0b0000 => return Err(FrameHeaderError::InvalidBlockSize),
0b0001 => 192,
n @ 0b010..=0b0101 => 576 * (1 << (n - 2)),
0b0110 => r.read::<u32>(8)? + 1,
0b0111 => r.read::<u32>(16)? + 1,
n @ 0b1000..=0b1111 => 256 * (1 << (n - 8)),
_ => unreachable!(),
},
sample_rate: match sample_rate_bits {
0b0000 => streaminfo.sample_rate,
0b0001 => 88200,
0b0010 => 176400,
0b0011 => 192000,
0b0100 => 8000,
0b0101 => 16000,
0b0110 => 22050,
0b0111 => 24000,
0b1000 => 32000,
0b1001 => 44100,
0b1010 => 48000,
0b1011 => 96000,
0b1100 => r.read::<u32>(8)? * 1000,
0b1101 => r.read::<u32>(16)?,
0b1110 => r.read::<u32>(16)? * 10,
0b1111 => return Err(FrameHeaderError::InvalidSampleRate),
_ => unreachable!(),
},
channel_assignment,
sample_size,
frame_number,
crc8: r.read(8)?
})
}
}
#[derive(Debug)]
enum FrameHeaderError {
Io(std::io::Error),
InvalidSync,
InvalidReservedBit,
InvalidSampleSize,
InvalidBlockSize,
InvalidSampleRate,
}
impl From<std::io::Error> for FrameHeaderError {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
fn read_utf8<R: BitRead + ?Sized>(r: &mut R) -> Result<u64, std::io::Error> {
r.read(8) // left unimplimented in this example
}
let mut reader = BitReader::endian(Cursor::new(b"\xFF\xF8\xC9\x18\x00\xC2"), BigEndian);
assert_eq!(
reader.parse_with::<FrameHeader>(&Streaminfo::default()).unwrap(),
FrameHeader {
variable_block_size: false,
block_size: 4096,
sample_rate: 44100,
channel_assignment: 1,
sample_size: 16,
frame_number: 0,
crc8: 0xC2,
}
);
§Example with lifetime-contrained Context
In some cases, the Context
can depend on a reference to another struct
.
use std::io::{Cursor, Read};
use bitstream_io::{BigEndian, BitRead, BitReader, FromBitStreamWith};
#[derive(Default)]
struct ModeParameters {
size_len: u8,
index_len: u8,
index_delta_len: u8,
// ...
}
struct AuHeaderParseContext<'a> {
params: &'a ModeParameters,
base_index: Option<u32>,
}
#[derive(Debug, PartialEq, Eq)]
struct AuHeader {
size: u32,
index: u32,
// ...
}
impl<'a> FromBitStreamWith<'a> for AuHeader {
type Context = AuHeaderParseContext<'a>;
type Error = AuHeaderError;
fn from_reader<R: BitRead + ?Sized>(
r: &mut R,
ctx: &AuHeaderParseContext<'a>,
) -> Result<Self, Self::Error> {
let size = r.read::<u32>(ctx.params.size_len as u32)?;
let index = match ctx.base_index {
None => r.read::<u32>(ctx.params.index_len as u32)?,
Some(base_index) => {
base_index
+ 1
+ r.read::<u32>(ctx.params.index_delta_len as u32)?
}
};
Ok(AuHeader {
size,
index,
// ...
})
}
}
#[derive(Debug)]
enum AuHeaderError {
Io(std::io::Error),
}
impl From<std::io::Error> for AuHeaderError {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
let mut reader = BitReader::endian(Cursor::new(b"\xFF\xEA\xFF\x10"), BigEndian);
let mode_params = ModeParameters {
size_len: 10,
index_len: 6,
index_delta_len: 2,
// ...
};
let mut ctx = AuHeaderParseContext {
params: &mode_params,
base_index: None,
};
let header1 = reader.parse_with::<AuHeader>(&ctx).unwrap();
assert_eq!(
header1,
AuHeader {
size: 1023,
index: 42,
}
);
ctx.base_index = Some(header1.index);
assert_eq!(
reader.parse_with::<AuHeader>(&ctx).unwrap(),
AuHeader {
size: 1020,
index: 44,
}
);