use nom::{
number::{streaming, Endianness},
sequence::tuple,
IResult, Needed,
};
use crate::{error::ParsingError, exif::TiffHeader, values::DataFormat, ExifTag};
use super::{exif_exif::IFD_ENTRY_SIZE, exif_iter::SUBIFD_TAGS};
pub(crate) struct IfdHeaderTravel<'a> {
ifd_data: &'a [u8],
offset: u32,
endian: Endianness,
}
#[derive(Debug, Clone)]
pub(crate) struct EntryInfo<'a> {
pub tag: u16,
#[allow(unused)]
pub data: &'a [u8],
#[allow(unused)]
pub data_format: DataFormat,
#[allow(unused)]
pub data_offset: Option<u32>,
pub sub_ifd_offset: Option<u32>,
}
impl<'a> IfdHeaderTravel<'a> {
pub fn new(input: &'a [u8], offset: u32, endian: Endianness) -> Self {
Self {
ifd_data: input,
endian,
offset,
}
}
#[tracing::instrument(skip_all)]
fn parse_tag_entry_header(
&'a self,
entry_data: &'a [u8],
) -> IResult<&'a [u8], Option<EntryInfo<'a>>> {
let endian = self.endian;
let (remain, (tag, data_format, components_num, value_or_offset)) = tuple((
streaming::u16::<_, nom::error::Error<_>>(endian),
streaming::u16(endian),
streaming::u32(endian),
streaming::u32(endian),
))(entry_data)?;
if tag == 0 {
return Ok((remain, None));
}
let data_format: DataFormat = match data_format.try_into() {
Ok(df) => df,
Err(e) => {
tracing::warn!(?e, "Ignored: IFD entry data format error");
return Ok((&[][..], None));
}
};
let component_size = data_format.component_size();
let size = components_num as usize * component_size;
let (data, data_offset) = if size > 4 {
let start = self.get_data_pos(value_or_offset) as usize;
let end = start + size;
tracing::debug!(
components_num,
size,
"tag {:04x} entry data @ offset {:08x} start {:08x} end {:08x} my_offset: {:08x} data len {:08x}",
tag,
value_or_offset,
start,
end,
self.offset,
self.ifd_data.len()
);
if end > self.ifd_data.len() {
return Err(nom::Err::Incomplete(Needed::new(end - self.ifd_data.len())));
}
(&self.ifd_data[start..end], Some(start as u32))
} else {
(entry_data, None)
};
let sub_ifd_offset = if SUBIFD_TAGS.contains(&tag) {
let offset = self.get_data_pos(value_or_offset);
Some(offset)
} else {
None
};
let entry = EntryInfo {
tag,
data,
data_format,
data_offset,
sub_ifd_offset,
};
Ok((&[][..], Some(entry)))
}
fn get_data_pos(&'a self, value_or_offset: u32) -> u32 {
value_or_offset.saturating_sub(self.offset)
}
fn parse_ifd_entry_header(&self, pos: u32) -> IResult<&[u8], Option<IfdHeaderTravel<'a>>> {
let (_, entry_data) =
nom::bytes::streaming::take(IFD_ENTRY_SIZE)(&self.ifd_data[pos as usize..])?;
let (remain, entry) = self.parse_tag_entry_header(entry_data)?;
if let Some(entry) = entry {
if let Some(offset) = entry.sub_ifd_offset {
let tag: ExifTag = entry.tag.try_into().unwrap();
println!("sub-ifd: {:?}", tag);
let sub_ifd =
IfdHeaderTravel::new(&self.ifd_data[offset as usize..], offset, self.endian);
return Ok((remain, Some(sub_ifd)));
}
}
Ok((remain, None))
}
#[tracing::instrument(skip(self))]
pub fn travel_ifd(&mut self, depth: usize) -> Result<(), ParsingError> {
if depth > 1 {
let msg = "depth shouldn't be greater than 1";
tracing::error!(msg);
debug_assert!(false, "{}", msg);
return Err(ParsingError::Failed(msg.into()));
}
tracing::debug!(ifd_data_len = self.ifd_data.len(), offset = self.offset);
let (remain, entry_num) = TiffHeader::parse_ifd_entry_num(self.ifd_data, self.endian)?;
let mut pos = self.ifd_data.len() - remain.len();
let mut sub_ifds = Vec::new();
for _ in 0..entry_num {
let (_, sub_ifd) = self.parse_ifd_entry_header(pos as u32)?;
pos += IFD_ENTRY_SIZE;
if let Some(ifd) = sub_ifd {
sub_ifds.push(ifd);
}
}
for mut ifd in sub_ifds {
ifd.travel_ifd(depth + 1)?;
}
Ok(())
}
}