use crate::core::{
ChannelData, FloFile, FloResult, Frame, FrameType, Header, ResidualEncoding, TocEntry,
};
use crate::MAGIC;
use alloc::vec;
use alloc::vec::Vec;
pub struct Reader;
impl Reader {
pub fn new() -> Self {
Reader
}
pub fn read(&self, data: &[u8]) -> FloResult<FloFile> {
let mut cursor = Cursor::new(data);
let magic = cursor.read_bytes(4)?;
if magic != MAGIC {
return Err("Invalid flo file: bad magic".into());
}
let header = self.read_header(&mut cursor)?;
let toc_size = usize::try_from(header.toc_size)
.map_err(|_| crate::core::FloError::from("TOC size does not fit in memory"))?;
let data_size = usize::try_from(header.data_size)
.map_err(|_| crate::core::FloError::from("DATA size does not fit in memory"))?;
let extra_size = usize::try_from(header.extra_size)
.map_err(|_| crate::core::FloError::from("EXTRA size does not fit in memory"))?;
let meta_size = usize::try_from(header.meta_size)
.map_err(|_| crate::core::FloError::from("META size does not fit in memory"))?;
let toc = self.read_toc(&mut cursor, toc_size)?;
let frames = self.read_data_chunk(&mut cursor, data_size, header.channels, &toc)?;
cursor.skip(extra_size)?;
let metadata = cursor.read_bytes(meta_size)?;
Ok(FloFile {
header,
toc,
frames,
extra: vec![],
metadata,
})
}
fn read_header(&self, cursor: &mut Cursor) -> FloResult<Header> {
Ok(Header {
version_major: cursor.read_u8()?,
version_minor: cursor.read_u8()?,
flags: cursor.read_u16_le()?,
sample_rate: cursor.read_u32_le()?,
channels: cursor.read_u8()?,
bit_depth: cursor.read_u8()?,
total_samples: cursor.read_u64_le()?,
compression_level: cursor.read_u8()?,
data_crc32: {
cursor.skip(3)?; cursor.read_u32_le()?
},
header_size: cursor.read_u64_le()?,
toc_size: cursor.read_u64_le()?,
data_size: cursor.read_u64_le()?,
extra_size: cursor.read_u64_le()?,
meta_size: cursor.read_u64_le()?,
})
}
fn read_toc(&self, cursor: &mut Cursor, toc_size: usize) -> FloResult<Vec<TocEntry>> {
if toc_size < 4 {
cursor.skip(toc_size)?;
return Ok(vec![]);
}
let toc_end = cursor.checked_end(toc_size)?;
let num_entries = cursor.read_u32_le()? as usize;
if num_entries > 100_000 {
return Err("Invalid TOC: too many entries".into());
}
let mut entries = Vec::with_capacity(num_entries);
for _ in 0..num_entries {
entries.push(TocEntry {
frame_index: cursor.read_u32_le()?,
byte_offset: cursor.read_u64_le()?,
frame_size: cursor.read_u32_le()?,
timestamp_ms: cursor.read_u32_le()?,
});
}
if cursor.pos > toc_end {
return Err("TOC entries exceed TOC size".into());
}
cursor.pos = toc_end;
Ok(entries)
}
fn read_data_chunk(
&self,
cursor: &mut Cursor,
data_size: usize,
channels: u8,
toc: &[TocEntry],
) -> FloResult<Vec<Frame>> {
let data_start = cursor.pos;
let data_end = cursor.checked_end(data_size)?;
let mut frames = Vec::with_capacity(toc.len());
for toc_entry in toc.iter() {
let byte_offset = usize::try_from(toc_entry.byte_offset)
.map_err(|_| crate::core::FloError::from("Frame offset does not fit in memory"))?;
let frame_start = data_start.checked_add(byte_offset).ok_or_else(|| {
crate::core::FloError::from("Frame offset overflows memory address space")
})?;
if frame_start >= data_end {
return Err("Frame offset is outside DATA chunk".into());
}
cursor.pos = frame_start;
let frame_size = toc_entry.frame_size as usize;
let frame_end = frame_start.checked_add(frame_size).ok_or_else(|| {
crate::core::FloError::from("Frame size overflows memory address space")
})?;
if frame_end > data_end {
return Err("Frame exceeds DATA chunk".into());
}
let frame = self.read_frame(cursor, channels, frame_size)?;
frames.push(frame);
}
cursor.pos = data_end;
Ok(frames)
}
fn read_frame(&self, cursor: &mut Cursor, channels: u8, frame_size: usize) -> FloResult<Frame> {
let frame_start = cursor.pos;
let frame_end = frame_start.checked_add(frame_size).ok_or_else(|| {
crate::core::FloError::from("Frame size overflows memory address space")
})?;
if frame_end > cursor.data.len() {
return Err("Frame exceeds input data".into());
}
let frame_type_byte = cursor.read_u8()?;
let frame_samples = cursor.read_u32_le()?;
let flags = cursor.read_u8()?;
let frame_type = FrameType::from(frame_type_byte);
let mut frame = Frame::new(frame_type_byte, frame_samples);
frame.flags = flags;
let num_channels_to_read = if frame_type == FrameType::Transform {
1
} else {
channels as usize
};
for _ch_idx in 0..num_channels_to_read {
let ch_size = cursor.read_u32_le()? as usize;
let ch_end = cursor.pos.checked_add(ch_size).ok_or_else(|| {
crate::core::FloError::from("Channel size overflows memory address space")
})?;
if ch_end > frame_end {
return Err("Channel exceeds frame".into());
}
let ch_data =
self.read_channel_data(cursor, frame_type, frame_samples as usize, ch_end)?;
frame.channels.push(ch_data);
cursor.pos = ch_end;
}
cursor.pos = frame_end;
Ok(frame)
}
fn read_channel_data(
&self,
cursor: &mut Cursor,
frame_type: FrameType,
frame_samples: usize,
channel_end: usize,
) -> FloResult<ChannelData> {
if frame_samples > 2_000_000 {
return Err("Invalid frame: too many samples".into());
}
match frame_type {
FrameType::Silence => Ok(ChannelData::new_silence()),
FrameType::Raw => {
let bytes_needed = frame_samples.saturating_mul(2);
let available = channel_end.saturating_sub(cursor.pos);
let bytes_to_read = bytes_needed.min(available);
let residuals = cursor.read_bytes(bytes_to_read)?;
Ok(ChannelData::new_raw(residuals))
}
FrameType::Transform => {
let remaining = channel_end.saturating_sub(cursor.pos);
let residuals = if remaining > 0 {
cursor.read_bytes(remaining)?
} else {
vec![]
};
Ok(ChannelData {
predictor_coeffs: vec![],
shift_bits: 0,
residual_encoding: ResidualEncoding::Raw,
rice_parameter: 0,
residuals,
})
}
_ if frame_type.is_alpc() => {
let order = cursor.read_u8()? as usize;
if order > 12 {
return Err("Invalid LPC order".into());
}
let mut predictor_coeffs = Vec::with_capacity(order);
for _ in 0..order {
if cursor
.pos
.checked_add(4)
.is_none_or(|end| end > channel_end)
{
break;
}
predictor_coeffs.push(cursor.read_i32_le()?);
}
let shift_bits = cursor.read_u8()?;
let residual_encoding_byte = cursor.read_u8()?;
let residual_encoding = ResidualEncoding::from(residual_encoding_byte);
let rice_parameter = if residual_encoding == ResidualEncoding::Rice {
cursor.read_u8()?
} else {
0
};
let remaining = channel_end.saturating_sub(cursor.pos);
let residuals = if remaining > 0 {
cursor.read_bytes(remaining)?
} else {
vec![]
};
Ok(ChannelData {
predictor_coeffs,
shift_bits,
residual_encoding,
rice_parameter,
residuals,
})
}
_ => Ok(ChannelData::new_silence()),
}
}
}
impl Default for Reader {
fn default() -> Self {
Self::new()
}
}
struct Cursor<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> Cursor<'a> {
fn new(data: &'a [u8]) -> Self {
Cursor { data, pos: 0 }
}
fn read_bytes(&mut self, count: usize) -> FloResult<Vec<u8>> {
let end = self.checked_end(count)?;
if end > self.data.len() {
return Err("Unexpected end of file".into());
}
let bytes = self.data[self.pos..end].to_vec();
self.pos = end;
Ok(bytes)
}
fn skip(&mut self, count: usize) -> FloResult<()> {
let end = self.checked_end(count)?;
if end > self.data.len() {
return Err("Unexpected end of file".into());
}
self.pos = end;
Ok(())
}
fn checked_end(&self, count: usize) -> FloResult<usize> {
self.pos
.checked_add(count)
.ok_or_else(|| "Input range overflows memory address space".into())
}
fn read_u8(&mut self) -> FloResult<u8> {
if self.pos >= self.data.len() {
return Err("Unexpected end of file".into());
}
let val = self.data[self.pos];
self.pos += 1;
Ok(val)
}
fn read_u16_le(&mut self) -> FloResult<u16> {
let bytes = self.read_bytes(2)?;
Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
}
fn read_u32_le(&mut self) -> FloResult<u32> {
let bytes = self.read_bytes(4)?;
Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
fn read_i32_le(&mut self) -> FloResult<i32> {
let bytes = self.read_bytes(4)?;
Ok(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
fn read_u64_le(&mut self) -> FloResult<u64> {
let bytes = self.read_bytes(8)?;
Ok(u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
}