use crate::{
decoder::EndianReader,
error::{
TiffError,
TiffFormatError::{self, FloatExpected, SignedIntegerExpected, UnsignedIntegerExpected},
TiffResult,
},
structs::{
Tag,
TagType::{
self,
},
},
util::fix_endianness,
};
use std::{
collections::BTreeMap,
io::{Read, Seek, SeekFrom},
};
pub type Directory = BTreeMap<Tag, IfdEntry>;
pub type TagData = ProcessedEntry;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Offset {
pub tag_type: TagType,
pub count: u64,
pub offset: u64,
}
#[derive(Debug, PartialEq, Clone)]
pub enum IfdEntry {
Offset(Offset),
Value(ProcessedEntry),
}
impl IfdEntry {
pub fn from_reader<R: Read + Seek>(r: &mut EndianReader<R>, bigtiff: bool) -> TiffResult<Self> {
let t_u16 = r.read_u16()?;
let tag_type =
TagType::from_u16(t_u16).ok_or(TiffFormatError::InvalidTagValueType(t_u16))?;
let count: u64 = if bigtiff {
r.read_u64()?
} else {
r.read_u32()?.into()
};
let Some(value_bytes) = count.checked_mul(tag_type.size().try_into()?) else {
return Err(TiffError::LimitsExceeded);
};
if (!bigtiff && value_bytes > 4) || value_bytes > 8 {
Ok(IfdEntry::Offset(Offset {
tag_type,
count,
offset: if bigtiff {
r.read_u64()?
} else {
r.read_u32()?.into()
},
}))
} else {
let mut offset = ProcessedEntry::new(tag_type, count.try_into()?);
r.read_exact(offset.buf_mut())?;
let rem = if bigtiff {
8 - offset.len() * tag_type.size()
} else {
4 - offset.len() * tag_type.size()
};
if rem != 0 {
r.seek(SeekFrom::Current(rem.try_into()?))?;
}
fix_endianness(
offset.buf_mut(),
r.byte_order,
8 * tag_type.primitive_size(),
);
Ok(IfdEntry::Value(offset))
}
}
}
#[derive(Debug, PartialEq, Clone)]
#[non_exhaustive]
pub enum ProcessedEntry {
Byte(Vec<u8>),
SByte(Vec<i8>),
Undefined(Vec<u8>),
Ascii(Vec<u8>),
Short(Vec<u16>),
SShort(Vec<i16>),
Long(Vec<u32>),
SLong(Vec<i32>),
Ifd(Vec<u32>),
Long8(Vec<u64>),
SLong8(Vec<i64>),
Ifd8(Vec<u64>),
Float(Vec<f32>),
Double(Vec<f64>),
Rational(Vec<u32>),
SRational(Vec<i32>),
}
impl ProcessedEntry {
#[rustfmt::skip]
pub fn new(tag_type: TagType, count: usize) -> Self {
match tag_type {
TagType::BYTE => Self::Byte (vec![0 ; count ]),
TagType::SBYTE => Self::SByte (vec![0 ; count ]),
TagType::UNDEFINED => Self::Undefined(vec![0 ; count ]),
TagType::ASCII => Self::Ascii (vec![0 ; count ]),
TagType::SHORT => Self::Short (vec![0 ; count ]),
TagType::SSHORT => Self::SShort (vec![0 ; count ]),
TagType::LONG => Self::Long (vec![0 ; count ]),
TagType::SLONG => Self::SLong (vec![0 ; count ]),
TagType::IFD => Self::Ifd (vec![0 ; count ]),
TagType::LONG8 => Self::Long8 (vec![0 ; count ]),
TagType::SLONG8 => Self::SLong8 (vec![0 ; count ]),
TagType::IFD8 => Self::Ifd8 (vec![0 ; count ]),
TagType::FLOAT => Self::Float (vec![0.0; count ]),
TagType::DOUBLE => Self::Double (vec![0.0; count ]),
TagType::RATIONAL => Self::Rational (vec![0 ; count * 2]),
TagType::SRATIONAL => Self::SRational(vec![0 ; count * 2]),
}
}
#[rustfmt::skip]
pub fn buf_mut(&mut self) -> &mut [u8] {
match self {
Self::Byte (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::SByte (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Undefined(v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Ascii (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Short (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::SShort (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Long (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::SLong (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Ifd (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Long8 (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::SLong8 (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Ifd8 (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Float (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Double (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::Rational (v) => bytemuck::cast_slice_mut(&mut v[..]),
Self::SRational(v) => bytemuck::cast_slice_mut(&mut v[..]),
}
}
#[rustfmt::skip]
pub fn len(&self) -> usize {
match self {
Self::Byte (v) => v.len(),
Self::SByte (v) => v.len(),
Self::Undefined(v) => v.len(),
Self::Ascii (v) => v.len(),
Self::Short (v) => v.len(),
Self::SShort (v) => v.len(),
Self::Long (v) => v.len(),
Self::SLong (v) => v.len(),
Self::Ifd (v) => v.len(),
Self::Long8 (v) => v.len(),
Self::SLong8 (v) => v.len(),
Self::Ifd8 (v) => v.len(),
Self::Float (v) => v.len(),
Self::Double (v) => v.len(),
Self::Rational (v) => v.len() / 2,
Self::SRational(v) => v.len() / 2,
}
}
}
macro_rules! impl_try_from_processed_entry {
(
$target:ty,
[$($from_small:ident),*],
$from_exact:ident,
[$($from_large:ident),*],
$error_type:ident
) => {
impl TryFrom<&ProcessedEntry> for $target {
type Error = TiffError;
fn try_from(val: &ProcessedEntry) -> Result<Self, Self::Error> {
if val.len() != 1 {
return Err(TiffFormatError::InconsistentSizesEncountered(val.clone()).into());
}
match val {
$(ProcessedEntry::$from_small(v) => Ok(Self::from(v[0])),)*
ProcessedEntry::$from_exact(v) => Ok(v[0]),
$(ProcessedEntry::$from_large(v) => Ok(Self::try_from(v[0])?),)*
_ => Err($error_type(val.clone()).into()),
}
}
}
impl TryFrom<ProcessedEntry> for $target {
type Error = TiffError;
fn try_from(val: ProcessedEntry) -> Result<Self, Self::Error> {
if val.len() != 1 {
return Err(TiffFormatError::InconsistentSizesEncountered(val.clone()).into());
}
match val {
$(ProcessedEntry::$from_small(v) => Ok(Self::from(v[0])),)*
ProcessedEntry::$from_exact(v) => Ok(v[0]),
$(ProcessedEntry::$from_large(v) => Ok(Self::try_from(v[0])?),)*
_ => Err($error_type(val.clone()).into()),
}
}
}
impl TryFrom<ProcessedEntry> for Vec<$target> {
type Error = TiffError;
fn try_from(val: ProcessedEntry) -> Result<Self, Self::Error> {
match val {
$(ProcessedEntry::$from_small(v) => Ok(v.into_iter().map(<$target>::from).collect()),)*
ProcessedEntry::$from_exact(v) => Ok(v),
$(ProcessedEntry::$from_large(v) => Ok(v.into_iter().map(<$target>::try_from).collect::<Result<Self, _>>()?),)*
_ => Err($error_type(val.clone()).into()),
}
}
}
impl<'a> TryFrom<&'a ProcessedEntry> for &'a[$target] {
type Error = TiffError;
fn try_from(val: &'a ProcessedEntry) -> Result<Self, Self::Error> {
match &val {
ProcessedEntry::$from_exact(v) => Ok(&v),
_ => Err($error_type(val.clone()).into()),
}
}
}
impl<'a> TryFrom<&'a mut ProcessedEntry> for &'a mut [$target] {
type Error = TiffError;
fn try_from(val: &'a mut ProcessedEntry) -> Result<Self, Self::Error> {
match val {
ProcessedEntry::$from_exact(ref mut v) => Ok(v),
_ => Err($error_type(val.clone()).into()),
}
}
}
impl From<&[$target]> for ProcessedEntry {
fn from(slice: &[$target]) -> Self {
ProcessedEntry::$from_exact(slice.to_vec())
}
}
impl From<Vec<$target>> for ProcessedEntry {
fn from(vec: Vec<$target>) -> Self {
ProcessedEntry::$from_exact(vec)
}
}
impl From<$target> for ProcessedEntry {
fn from(value: $target) -> Self {
ProcessedEntry::$from_exact(vec![value])
}
}
};
}
#[allow(unused_imports)]
pub use macro_impls::*;
#[rustfmt::skip]
mod macro_impls {
use super::*;
impl_try_from_processed_entry!(u8, [Ascii, Undefined], Byte,[Short, Ifd, Long, Ifd8, Long8] , UnsignedIntegerExpected);
impl_try_from_processed_entry!(u16, [Byte],Short,[Ifd, Long, Ifd8, Long8] , UnsignedIntegerExpected );
impl_try_from_processed_entry!(u32, [Byte, Short, Ifd],Long,[Ifd8, Long8] , UnsignedIntegerExpected );
impl_try_from_processed_entry!(u64, [Byte, Short, Ifd, Long, Ifd8],Long8, [], UnsignedIntegerExpected );
impl_try_from_processed_entry!(i8, [], SByte,[SShort, SLong, SLong8] , SignedIntegerExpected );
impl_try_from_processed_entry!(i16, [SByte],SShort,[SLong, SLong8] , SignedIntegerExpected);
impl_try_from_processed_entry!(i32, [SByte, SShort],SLong,[SLong8] , SignedIntegerExpected);
impl_try_from_processed_entry!(i64, [SByte, SShort, SLong],SLong8, [], SignedIntegerExpected);
impl_try_from_processed_entry!(f32, [], Float, [], FloatExpected);
impl_try_from_processed_entry!(f64, [Float], Double, [], FloatExpected);
}
impl TryFrom<&ProcessedEntry> for (u32, u32) {
type Error = TiffError;
fn try_from(val: &ProcessedEntry) -> Result<Self, Self::Error> {
if val.len() != 2 {
return Err(TiffFormatError::InconsistentSizesEncountered(val.clone()).into());
}
match &val {
ProcessedEntry::Rational(v) => Ok((v[0], v[1])),
_ => Err(TiffFormatError::RationalExpected(val.clone()).into()),
}
}
}
impl TryFrom<&ProcessedEntry> for (i32, i32) {
type Error = TiffError;
fn try_from(val: &ProcessedEntry) -> Result<Self, Self::Error> {
if val.len() != 2 {
return Err(TiffFormatError::InconsistentSizesEncountered(val.clone()).into());
}
match &val {
ProcessedEntry::SRational(v) => Ok((v[0], v[1])),
_ => Err(TiffFormatError::SignedRationalExpected(val.clone()).into()),
}
}
}
impl<'a> TryFrom<&'a ProcessedEntry> for &'a str {
type Error = TiffError;
fn try_from(val: &'a ProcessedEntry) -> Result<Self, Self::Error> {
match val {
ProcessedEntry::Byte(v) | ProcessedEntry::Ascii(v) | ProcessedEntry::Undefined(v) => {
if v.is_ascii() && v.ends_with(&[0]) {
let v = std::str::from_utf8(v)?;
let v = v.trim_matches(char::from(0));
Ok(v)
} else {
Err(TiffFormatError::InvalidTag.into())
}
}
_ => Err(TiffFormatError::AsciiExpected(val.clone()).into()),
}
}
}
#[allow(unused_imports, clippy::useless_conversion)]
mod test_entry {
use super::*;
use crate::ByteOrder;
use std::io;
use TagType::{
ASCII,
BYTE,
DOUBLE,
SBYTE,
UNDEFINED,
LONG,
SLONG,
FLOAT,
IFD,
SSHORT,
LONG8,
RATIONAL,
SRATIONAL,
IFD8,
SLONG8,
SHORT,
};
#[test]
fn test_bufferedentry_into_u8slice() {
let data = vec![42u8; 42];
let entry = ProcessedEntry::from(data.clone());
assert_eq!(<&[u8]>::try_from(&entry).unwrap(), data);
}
#[test]
#[rustfmt::skip]
fn test_single_fits_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,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Byte (vec![42]) ),
([ 0, 1, 0,0,0,1, 42, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Byte (vec![42]) ),
([ 6, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SByte (vec![42]) ),
([ 0, 6, 0,0,0,1, 42, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::SByte (vec![42]) ),
([ 7, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Undefined (vec![42]) ),
([ 0, 7, 0,0,0,1, 42, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Undefined (vec![42]) ),
([ 2, 0, 1,0,0,0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Ascii (vec![0 ]) ),
([ 0, 2, 0,0,0,1, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Ascii (vec![0 ]) ),
([ 3, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Short (vec![42]) ),
([ 0, 3, 0,0,0,1, 0,42, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Short (vec![42]) ),
([ 8, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SShort (vec![42]) ),
([ 0, 8, 0,0,0,1, 0,42, 0, 0], ByteOrder::BigEndian, ProcessedEntry::SShort (vec![42]) ),
([ 4, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Long (vec![42]) ),
([ 0, 4, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Long (vec![42]) ),
([ 9, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SLong (vec![42]) ),
([ 0, 9, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::SLong (vec![42]) ),
([13, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Ifd (vec![42]) ),
([ 0,13, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Ifd (vec![42]) ),
([11, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Float (vec![f32::from_bits(42)])),
([ 0,11, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Float (vec![f32::from_bits(42)])),
];
for (buf, byte_order, res) in cases {
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
assert_eq!(IfdEntry::from_reader(&mut r, false).unwrap(), IfdEntry::Value(res.try_into().unwrap()));
}
}
#[test]
#[rustfmt::skip]
fn test_single_fits_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, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Byte (vec![42]) ),
([ 0, 1, 0,0,0,0,0,0,0,1, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Byte (vec![42]) ),
([ 6, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SByte (vec![42]) ),
([ 0, 6, 0,0,0,0,0,0,0,1, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::SByte (vec![42]) ),
([ 7, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Undefined (vec![42]) ),
([ 0, 7, 0,0,0,0,0,0,0,1, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Undefined (vec![42]) ),
([ 2, 0, 1,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Ascii (vec![0 ]) ),
([ 0, 2, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Ascii (vec![0 ]) ),
([ 3, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Short (vec![42]) ),
([ 0, 3, 0,0,0,0,0,0,0,1, 0,42, 0, 0, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Short (vec![42]) ),
([ 8, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SShort (vec![42]) ),
([ 0, 8, 0,0,0,0,0,0,0,1, 0,42, 0, 0, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::SShort (vec![42]) ),
([ 4, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Long (vec![42]) ),
([ 0, 4, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Long (vec![42]) ),
([ 9, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SLong (vec![42]) ),
([ 0, 9, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::SLong (vec![42]) ),
([13, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Ifd (vec![42]) ),
([ 0,13, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Ifd (vec![42]) ),
([16, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Long8 (vec![42]) ),
([ 0,16, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Long8 (vec![42]) ),
([17, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SLong8 (vec![42]) ),
([ 0,17, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::SLong8 (vec![42]) ),
([18, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Ifd8 (vec![42]) ),
([ 0,18, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Ifd8 (vec![42]) ),
([11, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Float (vec![f32::from_bits(42)])),
([ 0,11, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0, 0], ByteOrder::BigEndian, ProcessedEntry::Float (vec![f32::from_bits(42)])),
([12, 0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Double (vec![f64::from_bits(42)])),
([ 0,12, 0,0,0,0,0,0,0,1, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Double (vec![f64::from_bits(42)])),
([ 5, 0, 1,0,0,0,0,0,0,0, 42,0, 0, 0,43, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Rational (vec![42, 43]) ),
([ 0, 5, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0,43], ByteOrder::BigEndian, ProcessedEntry::Rational (vec![42, 43]) ),
([ 10,0, 1,0,0,0,0,0,0,0, 42, 0, 0, 0,43, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SRational (vec![42, 43]) ),
([ 0,10, 0,0,0,0,0,0,0,1, 0, 0, 0,42, 0, 0, 0,43], ByteOrder::BigEndian, ProcessedEntry::SRational (vec![42, 43]) ),
];
for (buf, byte_order, res) in cases {
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
assert_eq!(IfdEntry::from_reader(&mut r, true).unwrap(), IfdEntry::Value(res.try_into().unwrap()));
}
}
#[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, 4,0,0,0, 42,42,42,42], ByteOrder::LittleEndian, ProcessedEntry::Byte (vec![42; 4]) ),
([0, 1, 0,0,0,4, 42,42,42,42], ByteOrder::BigEndian, ProcessedEntry::Byte (vec![42; 4]) ),
([6, 0, 4,0,0,0, 42,42,42,42], ByteOrder::LittleEndian, ProcessedEntry::SByte (vec![42; 4]) ),
([0, 6, 0,0,0,4, 42,42,42,42], ByteOrder::BigEndian, ProcessedEntry::SByte (vec![42; 4]) ),
([7, 0, 4,0,0,0, 42,42,42,42], ByteOrder::LittleEndian, ProcessedEntry::Undefined (vec![42; 4]) ),
([0, 7, 0,0,0,4, 42,42,42,42], ByteOrder::BigEndian, ProcessedEntry::Undefined (vec![42; 4]) ),
([2, 0, 4,0,0,0, 42,42,42, 0], ByteOrder::LittleEndian, ProcessedEntry::Ascii ("***\0".into())),
([0, 2, 0,0,0,4, 42,42,42, 0], ByteOrder::BigEndian, ProcessedEntry::Ascii ("***\0".into())),
([3, 0, 2,0,0,0, 42, 0,42, 0], ByteOrder::LittleEndian, ProcessedEntry::Short (vec![42; 2]) ),
([0, 3, 0,0,0,2, 0,42, 0,42], ByteOrder::BigEndian, ProcessedEntry::Short (vec![42; 2]) ),
([8, 0, 2,0,0,0, 42, 0,42, 0], ByteOrder::LittleEndian, ProcessedEntry::SShort (vec![42; 2]) ),
([0, 8, 0,0,0,2, 0,42, 0,42], ByteOrder::BigEndian, ProcessedEntry::SShort (vec![42; 2]) ),
([0, 2, 0,0,0,4, b'A',b'B',b'C',0], ByteOrder::BigEndian, ProcessedEntry::Ascii("ABC\0".into())),
];
for (buf, byte_order, res) in cases {
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
assert_eq!(IfdEntry::from_reader(&mut r, false).unwrap(), IfdEntry::Value(res.try_into().unwrap()));
}
}
#[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, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42,42], ByteOrder::LittleEndian, ProcessedEntry::Byte (vec![42 ; 8])),
([ 0, 1, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42,42], ByteOrder::BigEndian, ProcessedEntry::Byte (vec![42 ; 8])),
([ 6, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42,42], ByteOrder::LittleEndian, ProcessedEntry::SByte (vec![42 ; 8])),
([ 0, 6, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42,42], ByteOrder::BigEndian, ProcessedEntry::SByte (vec![42 ; 8])),
([ 7, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42,42], ByteOrder::LittleEndian, ProcessedEntry::Undefined (vec![42 ; 8])),
([ 0, 7, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42,42], ByteOrder::BigEndian, ProcessedEntry::Undefined (vec![42 ; 8])),
([ 2, 0, 8,0,0,0,0,0,0,0, 42,42,42,42,42,42,42, 0], ByteOrder::LittleEndian, ProcessedEntry::Ascii ("*******\0".into() )),
([ 0, 2, 0,0,0,0,0,0,0,8, 42,42,42,42,42,42,42, 0], ByteOrder::BigEndian, ProcessedEntry::Ascii ("*******\0".into() )),
([ 3, 0, 4,0,0,0,0,0,0,0, 42, 0,42, 0,42, 0,42, 0], ByteOrder::LittleEndian, ProcessedEntry::Short (vec![42 ; 4])),
([ 0, 3, 0,0,0,0,0,0,0,4, 0,42, 0,42, 0,42, 0,42], ByteOrder::BigEndian, ProcessedEntry::Short (vec![42 ; 4])),
([ 8, 0, 4,0,0,0,0,0,0,0, 42, 0,42, 0,42, 0,42, 0], ByteOrder::LittleEndian, ProcessedEntry::SShort (vec![42 ; 4])),
([ 0, 8, 0,0,0,0,0,0,0,4, 0,42, 0,42, 0,42, 0,42], ByteOrder::BigEndian, ProcessedEntry::SShort (vec![42 ; 4])),
([ 4, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Long (vec![42 ; 2])),
([ 0, 4, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Long (vec![42 ; 2])),
([ 9, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::SLong (vec![42 ; 2])),
([ 0, 9, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::SLong (vec![42 ; 2])),
([13, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Ifd (vec![42 ; 2])),
([ 0,13, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Ifd (vec![42 ; 2])),
([11, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0,42, 0, 0, 0], ByteOrder::LittleEndian, ProcessedEntry::Float (vec![f32::from_bits(42); 2])),
([ 0,11, 0,0,0,0,0,0,0,2, 0, 0, 0,42, 0, 0, 0,42], ByteOrder::BigEndian, ProcessedEntry::Float (vec![f32::from_bits(42); 2])),
];
for (buf, byte_order, res) in cases {
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
assert_eq!(IfdEntry::from_reader(&mut r, true).unwrap(), IfdEntry::Value(res.try_into().unwrap()));
}
}
#[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, 5,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 5, TagType::BYTE ),
([ 0, 1, 0,0,0,5, 0, 0, 0,42], ByteOrder::BigEndian , 5, TagType::BYTE ),
([ 6, 0, 5,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 5, TagType::SBYTE ),
([ 0, 6, 0,0,0,5, 0, 0, 0,42], ByteOrder::BigEndian , 5, TagType::SBYTE ),
([ 7, 0, 5,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 5, TagType::UNDEFINED ),
([ 0, 7, 0,0,0,5, 0, 0, 0,42], ByteOrder::BigEndian , 5, TagType::UNDEFINED ),
([ 2, 0, 5,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 5, TagType::ASCII ),
([ 0, 2, 0,0,0,5, 0, 0, 0,42], ByteOrder::BigEndian , 5, TagType::ASCII ),
([ 3, 0, 3,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::SHORT ),
([ 0, 3, 0,0,0,3, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::SHORT ),
([ 8, 0, 3,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::SSHORT ),
([ 0, 8, 0,0,0,3, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::SSHORT ),
([ 4, 0, 2,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::LONG ),
([ 0, 4, 0,0,0,2, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::LONG ),
([13, 0, 2,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::IFD ),
([ 0,13, 0,0,0,2, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::IFD ),
([ 9, 0, 2,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::SLONG ),
([ 0, 9, 0,0,0,2, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::SLONG ),
([ 11,0, 2,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::FLOAT ),
([ 0,11, 0,0,0,2, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::FLOAT ),
([ 12,0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 1, TagType::DOUBLE ),
([ 0,12, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian , 1, TagType::DOUBLE ),
([ 5, 0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 1, TagType::RATIONAL ),
([ 0, 5, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian , 1, TagType::RATIONAL ),
([ 10,0, 1,0,0,0, 42, 0, 0, 0], ByteOrder::LittleEndian, 1, TagType::SRATIONAL ),
([ 0,10, 0,0,0,1, 0, 0, 0,42], ByteOrder::BigEndian , 1, TagType::SRATIONAL ),
];
for (buf, byte_order, count, tag_type) in cases {
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
assert_eq!(IfdEntry::from_reader(&mut r, false).unwrap(), IfdEntry::Offset(Offset { tag_type, count, offset: 42 }));
}
}
#[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, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 9, TagType::BYTE ),
([ 0, 1, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 9, TagType::BYTE ),
([ 6, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 9, TagType::SBYTE ),
([ 0, 6, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 9, TagType::SBYTE ),
([ 7, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 9, TagType::UNDEFINED ),
([ 0, 7, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 9, TagType::UNDEFINED ),
([ 2, 0, 9,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 9, TagType::ASCII ),
([ 0, 2, 0,0,0,0,0,0,0,9, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 9, TagType::ASCII ),
([ 3, 0, 5,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 5, TagType::SHORT ),
([ 0, 3, 0,0,0,0,0,0,0,5, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 5, TagType::SHORT ),
([ 8, 0, 5,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 5, TagType::SSHORT ),
([ 0, 8, 0,0,0,0,0,0,0,5, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 5, TagType::SSHORT ),
([ 4, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::LONG ),
([ 0, 4, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::LONG ),
([ 9, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::SLONG ),
([ 0, 9, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::SLONG ),
([13, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::IFD ),
([ 0,13, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::IFD ),
([16, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::LONG8 ),
([ 0,16, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::LONG8 ),
([17, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::SLONG8 ),
([ 0,17, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::SLONG8 ),
([18, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::IFD8 ),
([ 0,18, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::IFD8 ),
([11, 0, 3,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 3, TagType::FLOAT ),
([ 0,11, 0,0,0,0,0,0,0,3, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 3, TagType::FLOAT ),
([12, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::DOUBLE ),
([ 0,12, 0,0,0,0,0,0,0,2, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::DOUBLE ),
([ 5, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::RATIONAL ),
([ 0, 5, 0,0,0,0,0,0,0,2, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::RATIONAL ),
([10, 0, 2,0,0,0,0,0,0,0, 42, 0, 0, 0, 0, 0, 0, 0], ByteOrder::LittleEndian, 2, TagType::SRATIONAL ),
([ 0,10, 0,0,0,0,0,0,0,2, 0, 0, 0, 0, 0, 0, 0,42], ByteOrder::BigEndian , 2, TagType::SRATIONAL ),
];
for (buf, byte_order, count, tag_type) in cases {
let mut r = EndianReader::wrap(io::Cursor::new(buf), byte_order);
assert_eq!(IfdEntry::from_reader(&mut r, true).unwrap(), IfdEntry::Offset(Offset { tag_type, count, offset: 42 }));
}
}
}