use crate::{BinTag4, Riff, RiffError, is, slice};
#[doc = crate::_tags!(data codec)]
#[doc = crate::_doc_meta!{location("data/codec/pack")}]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RiffChunk<'a> {
pub(super) id: BinTag4,
pub(super) size: u32,
pub(super) data: &'a [u8],
pub(super) offset: usize,
}
#[rustfmt::skip]
impl<'a> RiffChunk<'a> {
#[inline(always)]
pub const fn id(self) -> BinTag4 { self.id }
#[must_use] #[inline(always)]
pub const fn size(self) -> u32 { self.size }
#[must_use] #[inline(always)]
pub const fn len(self) -> usize { self.data.len() }
#[must_use] #[inline(always)]
pub const fn is_empty(self) -> bool { self.data.is_empty() }
#[must_use] #[inline(always)]
pub const fn data(self) -> &'a [u8] { self.data }
#[must_use] #[inline(always)]
pub const fn offset(self) -> usize { self.offset }
#[must_use] #[inline(always)]
pub const fn is_riff(self) -> bool { self.id.eq_bytes(*b"RIFF") }
#[must_use] #[inline(always)]
pub const fn is_rifx(self) -> bool { self.id.eq_bytes(*b"RIFX") }
#[must_use] #[inline(always)]
pub const fn is_list(self) -> bool { self.id.eq_bytes(*b"LIST") }
#[must_use] #[inline(always)]
pub const fn is_container(self) -> bool {
self.is_riff() || self.is_rifx() || self.is_list()
}
#[must_use]
pub const fn container_type(self) -> Option<BinTag4> {
is! { !self.is_container() || self.data.len() < 4, return None }
Some(BinTag4::new([self.data[0], self.data[1], self.data[2], self.data[3]]))
}
#[must_use]
pub const fn subchunk_data(self) -> Option<&'a [u8]> {
is! { !self.is_container() || self.data.len() < 4, return None }
Some(self.data.split_at(4).1)
}
pub const fn subchunks(self) -> Result<RiffChunkIter<'a>, RiffError> {
let Some(data) = self.subchunk_data() else { return Err(RiffError::MissingContainerType); };
Ok(Riff::chunks(data))
}
}
#[doc = crate::_tags!(data codec iterator)]
#[doc = crate::_doc_meta!{location("data/codec/pack")}]
#[must_use]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RiffChunkIter<'a> {
bytes: &'a [u8],
offset: usize,
}
#[rustfmt::skip]
impl<'a> RiffChunkIter<'a> {
pub(crate) const fn _new(bytes: &'a [u8], offset: usize) -> Self { Self { bytes, offset } }
#[inline(always)]
pub const fn new(bytes: &'a [u8]) -> Self { Self::_new(bytes, 0) }
#[must_use] #[inline(always)]
pub const fn remaining(self) -> &'a [u8] { slice![self.bytes, self.offset, ..] }
#[must_use] #[inline(always)]
pub const fn offset(self) -> usize { self.offset }
#[must_use] #[inline(always)]
pub const fn is_empty(self) -> bool { self.offset >= self.bytes.len() }
#[must_use]
pub const fn next_chunk(&mut self) -> Option<Result<RiffChunk<'a>, RiffError>> {
is![self.offset >= self.bytes.len(), return None];
match Riff::chunk_at(self.bytes, self.offset) {
Ok((chunk, next)) => { self.offset = next; Some(Ok(chunk)) }
Err(err) => { self.offset = self.bytes.len(); Some(Err(err)) }
}
}
pub const fn count_chunks(mut self) -> Result<usize, RiffError> {
let mut count = 0;
while let Some(result) = self.next_chunk() {
match result { Ok(_) => count += 1, Err(err) => return Err(err) }
}
Ok(count)
}
}
#[rustfmt::skip]
impl<'a> Iterator for RiffChunkIter<'a> {
type Item = Result<RiffChunk<'a>, RiffError>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> { self.next_chunk() }
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.bytes.len().saturating_sub(self.offset);
let max = if remaining == 0 { 0 } else { remaining / 8 + 1 };
(0, Some(max))
}
}
impl<'a> crate::IteratorFused for RiffChunkIter<'a> {}