use crate::{
decoder::EndianReader,
error::{TiffError, TiffFormatError, TiffResult, UsageError},
structs::{IfdEntry, Tag, TagData},
ByteOrder,
};
use log::debug;
use std::{
collections::BTreeMap,
io,
};
use super::entry::ProcessedEntry;
pub type Directory = BTreeMap<Tag, IfdEntry>;
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Ifd {
pub(crate) sub_ifds: Vec<Ifd>,
pub(crate) data: Directory,
}
impl Ifd {
pub fn from_buffer(
buf: &[u8],
byte_order: ByteOrder,
bigtiff: bool,
) -> TiffResult<(Self, u64)> {
let mut ifd = Ifd::default();
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
let num_entries: u64 = if bigtiff {
r.read_u64()?
} else {
r.read_u16()?.into()
};
debug!("num entries: {num_entries}");
for _ in 0..num_entries {
let tag = Tag::from_u16_exhaustive(r.read_u16()?);
ifd.data
.insert(tag, IfdEntry::from_reader(&mut r, bigtiff)?);
debug!("Added {:26} {:?}", format!("{:?}", tag), ifd.data[&tag]);
}
let next = if bigtiff {
r.read_u64()?
} else {
r.read_u32()?.into()
};
debug!("next ifd: {next}");
Ok((ifd, next))
}
pub fn get_tag(&self, tag: &Tag) -> Option<&IfdEntry> {
self.data.get(tag)
}
pub fn require_tag(&self, tag: &Tag) -> TiffResult<&IfdEntry> {
self.data.get(tag).ok_or(TiffError::FormatError(
TiffFormatError::RequiredTagNotFound(*tag),
))
}
pub fn remove_required_val(&mut self, tag: &Tag) -> TiffResult<TagData> {
match self
.data
.remove(tag)
.ok_or(TiffFormatError::RequiredTagNotFound(*tag))?
{
IfdEntry::Offset(o) => {
self.data.insert(*tag, IfdEntry::Offset(o));
Err(UsageError::RequiredTagNotLoaded(*tag, o).into())
}
IfdEntry::Value(be) => Ok(be),
}
}
pub fn remove_optional_val(&mut self, tag: &Tag) -> TiffResult<Option<TagData>> {
match self.data.remove(tag) {
Some(IfdEntry::Offset(o)) => {
self.data.insert(*tag, IfdEntry::Offset(o));
Err(UsageError::RequiredTagNotLoaded(*tag, o).into())
}
Some(IfdEntry::Value(v)) => Ok(Some(v)),
None => Ok(None),
}
}
pub fn require_tag_value(&self, tag: &Tag) -> TiffResult<&TagData> {
match self.require_tag(tag)? {
IfdEntry::Offset(o) => Err(UsageError::RequiredTagNotLoaded(*tag, *o).into()),
IfdEntry::Value(be) => Ok(be),
}
}
pub fn get_tag_value(&self, tag: &Tag) -> TiffResult<Option<&TagData>> {
if let Some(be) = self.get_tag(tag) {
match be {
IfdEntry::Offset(o) => Err(UsageError::RequiredTagNotLoaded(*tag, *o).into()),
IfdEntry::Value(be) => Ok(Some(be)),
}
} else {
Ok(None)
}
}
pub fn contains_key(&self, tag: &Tag) -> bool {
self.data.contains_key(tag)
}
pub fn insert_tag_data(&mut self, tag_data: BTreeMap<Tag, ProcessedEntry>) -> TiffResult<()> {
for (tag, value) in tag_data {
self.data
.insert(tag, IfdEntry::Value(value))
.ok_or(UsageError::TagOfDataNotPresent(tag))?;
}
Ok(())
}
}
impl From<Directory> for Ifd {
fn from(data: Directory) -> Self {
Self {
sub_ifds: Vec::new(),
data,
}
}
}
#[allow(unused_imports, clippy::useless_conversion)]
mod test_ifd {
use super::*;
use crate::structs::{value::Value, Offset, TagData, TagType};
#[test]
#[rustfmt::skip]
fn test_multitag() {
let cases = [
([2,0, 1,1, 1,0, 1,0,0,0, 42, 0, 0, 0,
0,1, 1,0, 1,0,0,0, 43, 0, 0, 0, 0,0,0,0], TagData::Byte(vec![42]), TagData::Byte(vec![43])),
([2,0, 1,1, 4,0, 1,0,0,0, 42, 0, 0, 0,
0,1, 4,0, 1,0,0,0, 43, 0, 0, 0, 0,0,0,0], TagData::Long(vec![42]), TagData::Long(vec![43])),
([2,0, 1,1, 9,0, 1,0,0,0, 42, 0, 0, 0,
0,1, 9,0, 1,0,0,0, 43, 0, 0, 0, 0,0,0,0], TagData::SLong(vec![42]), TagData::SLong(vec![43])),
([2,0, 1,1, 1,0, 4,0,0,0, 42,42,42,42,
0,1, 1,0, 4,0,0,0, 43,43,43,43, 0,0,0,0], TagData::Byte(vec![42;4]), TagData::Byte(vec![43;4])),
([2,0, 1,1, 1,0, 3,0,0,0, 42,42,42, 0,
0,1, 9,0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], TagData::Byte(vec![42;3]), TagData::SLong(vec![42])),
];
for (buf, res1, res2) in cases {
let t1 = Tag::from_u16_exhaustive(0x0101); let t2 = Tag::from_u16_exhaustive(0x0100); let mut dir = Directory::new();
dir.insert(t1, IfdEntry::Value(res1));
dir.insert(t2, IfdEntry::Value(res2));
assert_eq!(Ifd::from_buffer(&buf[..], ByteOrder::LittleEndian, false).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
#[test]
#[rustfmt::skip]
fn test_fits_single_notbig() {
assert_eq!(u16::from_le_bytes([42,0]),42);
assert_eq!(u16::from_be_bytes([0,42]),42);
assert_eq!(f32::from_le_bytes([0x42,0,0,0]),f32::from_bits(0x00_00_00_42));
assert_eq!(f32::from_be_bytes([0,0,0,0x42]),f32::from_bits(0x00_00_00_42));
let cases = [
([1,0, 1,1, 1, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Byte (vec![42]) ),
([0,1, 1,1, 0, 1, 0,0,0,1, 42, 0, 0, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::Byte (vec![42]) ),
([1,0, 1,1, 6, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::SByte (vec![42]) ),
([0,1, 1,1, 0, 6, 0,0,0,1, 42, 0, 0, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::SByte (vec![42]) ),
([1,0, 1,1, 7, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Undefined (vec![42]) ),
([0,1, 1,1, 0, 7, 0,0,0,1, 42, 0, 0, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::Undefined (vec![42]) ),
([1,0, 1,1, 2, 0, 1,0,0,0, 0, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Ascii (vec![0 ]) ),
([0,1, 1,1, 0, 2, 0,0,0,1, 0, 0, 0, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::Ascii (vec![0 ]) ),
([1,0, 1,1, 3, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Short (vec![42]) ),
([0,1, 1,1, 0, 3, 0,0,0,1, 0,42, 0, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::Short (vec![42]) ),
([1,0, 1,1, 8, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::SShort (vec![42]) ),
([0,1, 1,1, 0, 8, 0,0,0,1, 0,42, 0, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::SShort (vec![42]) ),
([1,0, 1,1, 4, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Long (vec![42]) ),
([0,1, 1,1, 0, 4, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian, TagData::Long (vec![42]) ),
([1,0, 1,1, 9, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::SLong (vec![42]) ),
([0,1, 1,1, 0, 9, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian, TagData::SLong (vec![42]) ),
([1,0, 1,1,13, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Ifd (vec![42]) ),
([0,1, 1,1, 0,13, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian, TagData::Ifd (vec![42]) ),
([1,0, 1,1,11, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Float (vec![f32::from_bits(42)])),
([0,1, 1,1, 0,11, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian, TagData::Float (vec![f32::from_bits(42)])),
];
for (buf, byte_order, res) in cases {
println!("Trying {buf:?}, with {byte_order:?} should become {res:?}");
let mut dir = Directory::new();
dir.insert(Tag::from_u16_exhaustive(0x01_01), IfdEntry::Value(res));
assert_eq!(Ifd::from_buffer(&buf, byte_order, false).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
#[test]
#[rustfmt::skip]
fn test_fits_single_big() {
assert_eq!(u16::from_le_bytes([42,0]),42);
assert_eq!(u16::from_be_bytes([0,42]),42);
assert_eq!(f32::from_le_bytes([0x42,0,0,0]),f32::from_bits(0x00_00_00_42));
assert_eq!(f32::from_be_bytes([0,0,0,0x42]),f32::from_bits(0x00_00_00_42));
let cases = [
([1,0,0,0,0,0,0,0, 1,1, 1, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Byte (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 1, 0,0,0,0,0,0,0,1, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Byte (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1, 6, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SByte (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 6, 0,0,0,0,0,0,0,1, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SByte (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1, 7, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Undefined (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 7, 0,0,0,0,0,0,0,1, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Undefined (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1, 2, 0, 1,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Ascii (vec![0 ]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 2, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Ascii (vec![0 ]) ),
([1,0,0,0,0,0,0,0, 1,1, 3, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Short (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 3, 0,0,0,0,0,0,0,1, 0,42, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Short (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1, 8, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SShort (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 8, 0,0,0,0,0,0,0,1, 0,42, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SShort (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1, 4, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Long (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 4, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Long (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1, 9, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SLong (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 9, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SLong (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1,13, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Ifd (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0,13, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Ifd (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1,16, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Long8 (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0,16, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Long8 (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1,17, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SLong8 (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0,17, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SLong8 (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1,18, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Ifd8 (vec![42]) ),
([0,0,0,0,0,0,0,1, 1,1, 0,18, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Ifd8 (vec![42]) ),
([1,0,0,0,0,0,0,0, 1,1,11, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Float (vec![f32::from_bits(42)])),
([0,0,0,0,0,0,0,1, 1,1, 0,11, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Float (vec![f32::from_bits(42)])),
([1,0,0,0,0,0,0,0, 1,1,12, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Double (vec![f64::from_bits(42)])),
([0,0,0,0,0,0,0,1, 1,1, 0,12, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Double (vec![f64::from_bits(42)])),
([1,0,0,0,0,0,0,0, 1,1, 5, 0, 1,0,0,0,0,0,0,0, 42,0, 0, 0,43, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Rational (vec![42, 43]) ),
([0,0,0,0,0,0,0,1, 1,1, 0, 5, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0,43, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Rational (vec![42, 43]) ),
([1,0,0,0,0,0,0,0, 1,1, 10,0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0,43, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SRational (vec![42, 43]) ),
([0,0,0,0,0,0,0,1, 1,1, 0,10, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0,43, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SRational (vec![42, 43]) ),
];
for (buf, byte_order, res) in cases {
println!(" tag type count offset");
println!(" |1 2 |1 2 |1 2 3 4 5 6 7 8 |1 2 3 4 5 6 7 8|");
println!("Trying {buf:?}, with {byte_order:?} should become {res:?}");
let mut dir = Directory::new();
dir.insert(Tag::from_u16_exhaustive(0x01_01), IfdEntry::Value(res.try_into().unwrap()));
assert_eq!(Ifd::from_buffer(&buf, byte_order, true).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
#[test]
#[rustfmt::skip]
fn test_fits_multi_notbig() {
assert_eq!(u16::from_le_bytes([42,0]),42);
assert_eq!(u16::from_be_bytes([0,42]),42);
assert_eq!(f32::from_le_bytes([0x42,0,0,0]),f32::from_bits(0x00_00_00_42));
assert_eq!(f32::from_be_bytes([0,0,0,0x42]),f32::from_bits(0x00_00_00_42));
let cases = [
([1,0, 1,1, 1, 0, 4,0,0,0, 42,42,42,42, 0,0,0,0], ByteOrder::LittleEndian, TagData::Byte (vec![42; 4]) ),
([0,1, 1,1, 0, 1, 0,0,0,4, 42,42,42,42, 0,0,0,0], ByteOrder::BigEndian, TagData::Byte (vec![42; 4]) ),
([1,0, 1,1, 6, 0, 4,0,0,0, 42,42,42,42, 0,0,0,0], ByteOrder::LittleEndian, TagData::SByte (vec![42; 4]) ),
([0,1, 1,1, 0, 6, 0,0,0,4, 42,42,42,42, 0,0,0,0], ByteOrder::BigEndian, TagData::SByte (vec![42; 4]) ),
([1,0, 1,1, 7, 0, 4,0,0,0, 42,42,42,42, 0,0,0,0], ByteOrder::LittleEndian, TagData::Undefined (vec![42; 4]) ),
([0,1, 1,1, 0, 7, 0,0,0,4, 42,42,42,42, 0,0,0,0], ByteOrder::BigEndian, TagData::Undefined (vec![42; 4]) ),
([1,0, 1,1, 2, 0, 4,0,0,0, 42,42,42, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Ascii ("***\0".into())),
([0,1, 1,1, 0, 2, 0,0,0,4, 42,42,42, 0, 0,0,0,0], ByteOrder::BigEndian, TagData::Ascii ("***\0".into())),
([1,0, 1,1, 3, 0, 2,0,0,0, 42, 0,42, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::Short (vec![42; 2]) ),
([0,1, 1,1, 0, 3, 0,0,0,2, 0,42, 0,42, 0,0,0,0], ByteOrder::BigEndian, TagData::Short (vec![42; 2]) ),
([1,0, 1,1, 8, 0, 2,0,0,0, 42, 0,42, 0, 0,0,0,0], ByteOrder::LittleEndian, TagData::SShort (vec![42; 2]) ),
([0,1, 1,1, 0, 8, 0,0,0,2, 0,42, 0,42, 0,0,0,0], ByteOrder::BigEndian, TagData::SShort (vec![42; 2]) ),
([0,1, 1,1, 0, 2, 0,0,0,4, b'A',b'B',b'C',0, 0,0,0,0], ByteOrder::BigEndian, TagData::Ascii("ABC\0".into())),
];
for (buf, byte_order, res) in cases {
println!("Trying {buf:?}, with {byte_order:?} should become {res:?}");
let mut dir = Directory::new();
dir.insert(Tag::from_u16_exhaustive(0x01_01), IfdEntry::Value(res.try_into().unwrap()));
assert_eq!(Ifd::from_buffer(&buf, byte_order, false).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
#[test]
#[rustfmt::skip]
fn test_fits_multi_big() {
assert_eq!(u16::from_le_bytes([42,0]),42);
assert_eq!(u16::from_be_bytes([0,42]),42);
assert_eq!(f32::from_le_bytes([0x42,0,0,0]),f32::from_bits(0x00_00_00_42));
assert_eq!(f32::from_be_bytes([0,0,0,0x42]),f32::from_bits(0x00_00_00_42));
let cases = [
([1,0,0,0,0,0,0,0, 1,1, 1, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42,42, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Byte (vec![42 ; 8])),
([0,0,0,0,0,0,0,1, 1,1, 0, 1, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Byte (vec![42 ; 8])),
([1,0,0,0,0,0,0,0, 1,1, 6, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42,42, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SByte (vec![42 ; 8])),
([0,0,0,0,0,0,0,1, 1,1, 0, 6, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SByte (vec![42 ; 8])),
([1,0,0,0,0,0,0,0, 1,1, 7, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42,42, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Undefined (vec![42 ; 8])),
([0,0,0,0,0,0,0,1, 1,1, 0, 7, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Undefined (vec![42 ; 8])),
([1,0,0,0,0,0,0,0, 1,1, 2, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Ascii ("*******\0".into() )),
([0,0,0,0,0,0,0,1, 1,1, 0, 2, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42, 0, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Ascii ("*******\0".into() )),
([1,0,0,0,0,0,0,0, 1,1, 3, 0, 4,0,0,0,0,0,0,0, 42, 0,42, 0,42, 0,42, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Short (vec![42 ; 4])),
([0,0,0,0,0,0,0,1, 1,1, 0, 3, 0,0,0,0,0,0,0,4, 0,42, 0,42, 0,42, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Short (vec![42 ; 4])),
([1,0,0,0,0,0,0,0, 1,1, 8, 0, 4,0,0,0,0,0,0,0, 42, 0,42, 0,42, 0,42, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SShort (vec![42 ; 4])),
([0,0,0,0,0,0,0,1, 1,1, 0, 8, 0,0,0,0,0,0,0,4, 0,42, 0,42, 0,42, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SShort (vec![42 ; 4])),
([1,0,0,0,0,0,0,0, 1,1, 4, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Long (vec![42 ; 2])),
([0,0,0,0,0,0,0,1, 1,1, 0, 4, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Long (vec![42 ; 2])),
([1,0,0,0,0,0,0,0, 1,1, 9, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::SLong (vec![42 ; 2])),
([0,0,0,0,0,0,0,1, 1,1, 0, 9, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::SLong (vec![42 ; 2])),
([1,0,0,0,0,0,0,0, 1,1,13, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Ifd (vec![42 ; 2])),
([0,0,0,0,0,0,0,1, 1,1, 0,13, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Ifd (vec![42 ; 2])),
([1,0,0,0,0,0,0,0, 1,1,11, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, TagData::Float (vec![f32::from_bits(42); 2])),
([0,0,0,0,0,0,0,1, 1,1, 0,11, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian, TagData::Float (vec![f32::from_bits(42); 2])),
];
for (buf, byte_order, res) in cases {
println!(" tag type count offset");
println!(" |1 2 |1 2 |1 2 3 4 5 6 7 8 |1 2 3 4 5 6 7 8|");
println!("Trying {buf:?}, with {byte_order:?} should become {res:?}");
let mut dir = Directory::new();
dir.insert(Tag::from_u16_exhaustive(0x01_01), IfdEntry::Value(res.try_into().unwrap()));
assert_eq!(Ifd::from_buffer(&buf, byte_order, true).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
#[test]
#[rustfmt::skip]
fn test_notfits_notbig() {
assert_eq!(u16::from_le_bytes([42,0]),42);
assert_eq!(u16::from_be_bytes([0,42]),42);
assert_eq!(f32::from_le_bytes([0x42,0,0,0]),f32::from_bits(0x00_00_00_42));
assert_eq!(f32::from_be_bytes([0,0,0,0x42]),f32::from_bits(0x00_00_00_42));
let cases = [
([1,0, 1,1, 1, 0, 5,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 5, TagType::BYTE ),
([0,1, 1,1, 0, 1, 0,0,0,5, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 5, TagType::BYTE ),
([1,0, 1,1, 6, 0, 5,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 5, TagType::SBYTE ),
([0,1, 1,1, 0, 6, 0,0,0,5, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 5, TagType::SBYTE ),
([1,0, 1,1, 7, 0, 5,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 5, TagType::UNDEFINED ),
([0,1, 1,1, 0, 7, 0,0,0,5, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 5, TagType::UNDEFINED ),
([1,0, 1,1, 2, 0, 5,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 5, TagType::ASCII ),
([0,1, 1,1, 0, 2, 0,0,0,5, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 5, TagType::ASCII ),
([1,0, 1,1, 3, 0, 3,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 3, TagType::SHORT ),
([0,1, 1,1, 0, 3, 0,0,0,3, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 3, TagType::SHORT ),
([1,0, 1,1, 8, 0, 3,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 3, TagType::SSHORT ),
([0,1, 1,1, 0, 8, 0,0,0,3, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 3, TagType::SSHORT ),
([1,0, 1,1, 4, 0, 2,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 2, TagType::LONG ),
([0,1, 1,1, 0, 4, 0,0,0,2, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 2, TagType::LONG ),
([1,0, 1,1,13, 0, 2,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 2, TagType::IFD ),
([0,1, 1,1, 0,13, 0,0,0,2, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 2, TagType::IFD ),
([1,0, 1,1, 9, 0, 2,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 2, TagType::SLONG ),
([0,1, 1,1, 0, 9, 0,0,0,2, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 2, TagType::SLONG ),
([1,0, 1,1, 11,0, 2,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 2, TagType::FLOAT ),
([0,1, 1,1, 0,11, 0,0,0,2, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 2, TagType::FLOAT ),
([1,0, 1,1, 12,0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 1, TagType::DOUBLE ),
([0,1, 1,1, 0,12, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 1, TagType::DOUBLE ),
([1,0, 1,1, 5, 0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 1, TagType::RATIONAL ),
([0,1, 1,1, 0, 5, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 1, TagType::RATIONAL ),
([1,0, 1,1, 10,0, 1,0,0,0, 42, 0, 0, 0, 0,0,0,0], ByteOrder::LittleEndian, 1, TagType::SRATIONAL ),
([0,1, 1,1, 0,10, 0,0,0,1, 0, 0, 0,42, 0,0,0,0], ByteOrder::BigEndian , 1, TagType::SRATIONAL ),
];
for (buf, byte_order, count, tag_type) in cases {
println!("Trying {buf:?}, with {byte_order:?}");
let mut dir = Directory::new();
dir.insert(Tag::from_u16_exhaustive(0x01_01), IfdEntry::Offset(Offset { tag_type, count, offset: 42 }));
assert_eq!(Ifd::from_buffer(&buf, byte_order, false).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
#[test]
#[rustfmt::skip]
fn test_notfits_big() {
assert_eq!(u16::from_le_bytes([42,0]),42);
assert_eq!(u16::from_be_bytes([0,42]),42);
assert_eq!(f32::from_le_bytes([0x42,0,0,0]),f32::from_bits(0x00_00_00_42));
assert_eq!(f32::from_be_bytes([0,0,0,0x42]),f32::from_bits(0x00_00_00_42));
let cases = [
([1,0,0,0,0,0,0,0, 1,1, 1, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 9, TagType::BYTE ),
([0,0,0,0,0,0,0,1, 1,1, 0, 1, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 9, TagType::BYTE ),
([1,0,0,0,0,0,0,0, 1,1, 6, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 9, TagType::SBYTE ),
([0,0,0,0,0,0,0,1, 1,1, 0, 6, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 9, TagType::SBYTE ),
([1,0,0,0,0,0,0,0, 1,1, 7, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 9, TagType::UNDEFINED ),
([0,0,0,0,0,0,0,1, 1,1, 0, 7, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 9, TagType::UNDEFINED ),
([1,0,0,0,0,0,0,0, 1,1, 2, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 9, TagType::ASCII ),
([0,0,0,0,0,0,0,1, 1,1, 0, 2, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 9, TagType::ASCII ),
([1,0,0,0,0,0,0,0, 1,1, 3, 0, 5,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 5, TagType::SHORT ),
([0,0,0,0,0,0,0,1, 1,1, 0, 3, 0,0,0,0,0,0,0,5, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 5, TagType::SHORT ),
([1,0,0,0,0,0,0,0, 1,1, 8, 0, 5,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 5, TagType::SSHORT ),
([0,0,0,0,0,0,0,1, 1,1, 0, 8, 0,0,0,0,0,0,0,5, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 5, TagType::SSHORT ),
([1,0,0,0,0,0,0,0, 1,1, 4, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::LONG ),
([0,0,0,0,0,0,0,1, 1,1, 0, 4, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::LONG ),
([1,0,0,0,0,0,0,0, 1,1, 9, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::SLONG ),
([0,0,0,0,0,0,0,1, 1,1, 0, 9, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::SLONG ),
([1,0,0,0,0,0,0,0, 1,1,13, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::IFD ),
([0,0,0,0,0,0,0,1, 1,1, 0,13, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::IFD ),
([1,0,0,0,0,0,0,0, 1,1,16, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::LONG8 ),
([0,0,0,0,0,0,0,1, 1,1, 0,16, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::LONG8 ),
([1,0,0,0,0,0,0,0, 1,1,17, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::SLONG8 ),
([0,0,0,0,0,0,0,1, 1,1, 0,17, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::SLONG8 ),
([1,0,0,0,0,0,0,0, 1,1,18, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::IFD8 ),
([0,0,0,0,0,0,0,1, 1,1, 0,18, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::IFD8 ),
([1,0,0,0,0,0,0,0, 1,1,11, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 3, TagType::FLOAT ),
([0,0,0,0,0,0,0,1, 1,1, 0,11, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 3, TagType::FLOAT ),
([1,0,0,0,0,0,0,0, 1,1,12, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 2, TagType::DOUBLE ),
([0,0,0,0,0,0,0,1, 1,1, 0,12, 0,0,0,0,0,0,0,2, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 2, TagType::DOUBLE ),
([1,0,0,0,0,0,0,0, 1,1, 5, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 2, TagType::RATIONAL ),
([0,0,0,0,0,0,0,1, 1,1, 0, 5, 0,0,0,0,0,0,0,2, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 2, TagType::RATIONAL ),
([1,0,0,0,0,0,0,0, 1,1,10, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0], ByteOrder::LittleEndian, 2, TagType::SRATIONAL ),
([0,0,0,0,0,0,0,1, 1,1, 0,10, 0,0,0,0,0,0,0,2, 0, 0, 0, 0, 0, 0, 0,42, 0,0,0,0,0,0,0,0], ByteOrder::BigEndian , 2, TagType::SRATIONAL ),
];
for (buf, byte_order, count, tag_type) in cases {
println!(" tag type count offset");
println!(" |1 2 |1 2 |1 2 3 4 5 6 7 8 |1 2 3 4 5 6 7 8|");
println!("Trying {buf:?}, with {byte_order:?}");
let mut dir = Directory::new();
dir.insert(Tag::from_u16_exhaustive(0x01_01), IfdEntry::Offset(Offset{ tag_type, count, offset: 42 }));
assert_eq!(Ifd::from_buffer(&buf, byte_order, true).unwrap(), (Ifd{
sub_ifds: Vec::new(),
data: dir
},0));
}
}
}