From 74459240a5cbe6d2aeacbc3f341fc20e69c32744 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sat, 30 Nov 2024 03:22:30 +0800 Subject: [PATCH 1/2] Fix clippy lints --- .gitignore | 2 ++ src/isobmff.rs | 19 +++++++++---------- src/reader.rs | 10 ++++++++-- src/tag.rs | 12 ++++++------ src/tiff.rs | 15 +++++++++------ src/util.rs | 4 ++-- src/value.rs | 18 +++++++++--------- src/writer.rs | 46 ++++++++++++++++++++++++++-------------------- tests/rwrcmp.rs | 20 ++++++++++---------- 9 files changed, 81 insertions(+), 65 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b37adf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +/Cargo.lock diff --git a/src/isobmff.rs b/src/isobmff.rs index 43659a6..926dba5 100644 --- a/src/isobmff.rs +++ b/src/isobmff.rs @@ -88,7 +88,7 @@ struct Location { impl Parser where R: BufRead + Seek { fn new(reader: R) -> Self { Self { - reader: reader, + reader, ftyp_checked: false, item_id: None, item_location: None, @@ -127,7 +127,7 @@ impl Parser where R: BufRead + Seek { let mut buf = [0; 8]; self.reader.read_exact(&mut buf)?; let size = match BigEndian::loadu32(&buf, 0) { - 0 => Some(std::u64::MAX), + 0 => Some(u64::MAX), 1 => read64(&mut self.reader)?.checked_sub(16), x => u64::from(x).checked_sub(8), }.ok_or("Invalid box size")?; @@ -138,7 +138,7 @@ impl Parser where R: BufRead + Seek { fn read_file_level_box(&mut self, size: u64) -> Result, Error> { let mut buf; match size { - std::u64::MAX => { + u64::MAX => { buf = Vec::new(); self.reader.read_to_end(&mut buf)?; }, @@ -154,7 +154,7 @@ impl Parser where R: BufRead + Seek { fn skip_file_level_box(&mut self, size: u64) -> Result<(), Error> { match size { - std::u64::MAX => self.reader.seek(SeekFrom::End(0))?, + u64::MAX => self.reader.seek(SeekFrom::End(0))?, _ => self.reader.seek(SeekFrom::Current( size.try_into().or(Err("Large seek not supported"))?))?, }; @@ -164,7 +164,7 @@ impl Parser where R: BufRead + Seek { fn parse_ftyp(&mut self, mut boxp: BoxSplitter) -> Result<(), Error> { let head = boxp.slice(8)?; let _major_brand = &head[0..4]; - let _minor_version = BigEndian::loadu32(&head, 4); + let _minor_version = BigEndian::loadu32(head, 4); while let Ok(compat_brand) = boxp.array4() { if HEIF_BRANDS.contains(&compat_brand) { return Ok(()); @@ -301,9 +301,8 @@ impl Parser where R: BufRead + Seek { }; for _ in 0..entry_count { let (boxtype, body) = boxp.child_box()?; - match boxtype { - b"infe" => self.parse_infe(body)?, - _ => {}, + if boxtype == b"infe" { + self.parse_infe(body)? } } Ok(()) @@ -483,7 +482,7 @@ mod tests { // to the end of the file let mut p = Parser::new(Cursor::new(b"\0\0\0\0abcd")); assert_eq!(p.read_box_header().unwrap(), - Some((std::u64::MAX, *b"abcd"))); + Some((u64::MAX, *b"abcd"))); // largesize let mut p = Parser::new(Cursor::new( b"\0\0\0\x01abcd\0\0\0\0\0\0\0\x10")); @@ -498,7 +497,7 @@ mod tests { let mut p = Parser::new(Cursor::new( b"\0\0\0\x01abcd\xff\xff\xff\xff\xff\xff\xff\xff")); assert_eq!(p.read_box_header().unwrap(), - Some((std::u64::MAX.wrapping_sub(16), *b"abcd"))); + Some((u64::MAX.wrapping_sub(16), *b"abcd"))); } #[test] diff --git a/src/reader.rs b/src/reader.rs index 23299a4..6b12ba7 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -63,6 +63,12 @@ pub struct Reader { continue_on_error: bool, } +impl Default for Reader { + fn default() -> Self { + Self::new() + } +} + impl Reader { /// Constructs a new `Reader`. pub fn new() -> Self { @@ -106,14 +112,14 @@ impl Reader { /// If an error occurred, `exif::Error` is returned. pub fn read_raw(&self, data: Vec) -> Result { let mut parser = tiff::Parser::new(); - parser.continue_on_error = self.continue_on_error.then(|| Vec::new()); + parser.continue_on_error = self.continue_on_error.then(Vec::new); parser.parse(&data)?; let entry_map = parser.entries.iter().enumerate() .map(|(i, e)| (e.ifd_num_tag(), i)).collect(); let exif = Exif { buf: data, entries: parser.entries, - entry_map: entry_map, + entry_map, little_endian: parser.little_endian, }; match parser.continue_on_error { diff --git a/src/tag.rs b/src/tag.rs index 5f65d26..356e3f7 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -749,10 +749,10 @@ generate_well_known_tag_constants!( ); // For Value::display_as(). -pub fn display_value_as<'a>(value: &'a Value, tag: Tag) -> value::Display<'a> { +pub fn display_value_as(value: &Value, tag: Tag) -> value::Display<'_> { match get_tag_info(tag) { - Some(ti) => value::Display { fmt: ti.dispval, value: value }, - None => value::Display { fmt: d_default, value: value }, + Some(ti) => value::Display { fmt: ti.dispval, value }, + None => value::Display { fmt: d_default, value }, } } @@ -1048,13 +1048,13 @@ fn d_subjarea(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result { // Acceleration (Exif 0x9404), CameraElevationAngle (Exif 0x9405) fn d_optdecimal(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result { match *value { - Value::Rational(ref v) if v.len() > 0 => + Value::Rational(ref v) if !v.is_empty() => if v[0].denom != 0xffffffff { write!(w, "{}", v[0].to_f64()) } else { w.write_str("unknown") }, - Value::SRational(ref v) if v.len() > 0 => + Value::SRational(ref v) if !v.is_empty() => if v[0].denom != -1 { write!(w, "{}", v[0].to_f64()) } else { @@ -1463,7 +1463,7 @@ where I: IntoIterator, T: fmt::Display { struct AsciiDisplay<'a>(&'a [u8]); -impl<'a> fmt::Display for AsciiDisplay<'a> { +impl fmt::Display for AsciiDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { d_sub_ascii(f, self.0) } diff --git a/src/tiff.rs b/src/tiff.rs index a3782f3..c3bb547 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -239,7 +239,7 @@ impl Parser { } let entry = Self::parse_ifd_entry::(data, offset); offset += 12; - let (tag, val) = match entry { + let (tag, value) = match entry { Ok(x) => x, Err(e) => { self.check_error(e)?; @@ -256,11 +256,11 @@ impl Parser { Tag::InteropIFDPointer => Context::Interop, _ => { self.entries.push(IfdEntry { field: Field { - tag: tag, ifd_num: In(ifd_num), value: val }.into()}); + tag, ifd_num: In(ifd_num), value }.into()}); continue; }, }; - self.parse_child_ifd::(data, val, child_ctx, ifd_num) + self.parse_child_ifd::(data, value, child_ctx, ifd_num) .or_else(|e| self.check_error(e))?; } @@ -313,7 +313,10 @@ impl Parser { fn check_error(&mut self, err: Error) -> Result<(), Error> { match self.continue_on_error { - Some(ref mut v) => Ok(v.push(err)), + Some(ref mut v) => { + v.push(err); + Ok(()) + }, None => Err(err), } } @@ -502,12 +505,12 @@ impl<'a> DisplayValue<'a> { ifd_num: self.ifd_num, value_display: self.value_display, unit: self.tag.unit(), - unit_provider: unit_provider, + unit_provider, } } } -impl<'a> fmt::Display for DisplayValue<'a> { +impl fmt::Display for DisplayValue<'_> { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.value_display.fmt(f) diff --git a/src/util.rs b/src/util.rs index 2da8ba0..f6eb3a9 100644 --- a/src/util.rs +++ b/src/util.rs @@ -58,7 +58,7 @@ impl BufReadExt for T where T: io::BufRead { fn discard_exact(&mut self, mut len: usize) -> io::Result<()> { while len > 0 { let consume_len = match self.fill_buf() { - Ok(buf) if buf.is_empty() => + Ok([]) => return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "unexpected EOF")), Ok(buf) => buf.len().min(len), @@ -106,7 +106,7 @@ impl ReadExt for T where T: io::Read { // This function must not be called with more than 4 bytes. pub fn atou16(bytes: &[u8]) -> Result { debug_assert!(bytes.len() <= 4); - if bytes.len() == 0 { + if bytes.is_empty() { return Err(Error::InvalidFormat("Not a number")); } let mut n = 0; diff --git a/src/value.rs b/src/value.rs index 1e684a0..503636d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -180,7 +180,7 @@ impl Value { Value::Short(ref v) => Some(UIntIter { iter: Box::new(v.iter().map(|&x| x as u32)) }), Value::Long(ref v) => - Some(UIntIter { iter: Box::new(v.iter().map(|&x| x)) }), + Some(UIntIter { iter: Box::new(v.iter().copied()) }), _ => None, } } @@ -213,7 +213,7 @@ impl UIntValue { match self.0 { Value::Byte(ref v) => v.get(index).map(|&x| x.into()), Value::Short(ref v) => v.get(index).map(|&x| x.into()), - Value::Long(ref v) => v.get(index).map(|&x| x), + Value::Long(ref v) => v.get(index).copied(), _ => panic!(), } } @@ -224,7 +224,7 @@ pub struct UIntIter<'a> { iter: Box + 'a> } -impl<'a> Iterator for UIntIter<'a> { +impl Iterator for UIntIter<'_> { type Item = u32; #[inline] @@ -238,7 +238,7 @@ impl<'a> Iterator for UIntIter<'a> { } } -impl<'a> ExactSizeIterator for UIntIter<'a> {} +impl ExactSizeIterator for UIntIter<'_> {} /// Helper struct for printing a value in a tag-specific format. #[derive(Copy, Clone)] @@ -247,7 +247,7 @@ pub struct Display<'a> { pub value: &'a Value, } -impl<'a> fmt::Display for Display<'a> { +impl fmt::Display for Display<'_> { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (self.fmt)(f, self.value) @@ -292,7 +292,7 @@ where F: Fn() -> T, T: Iterator, I: fmt::Debug { struct AsciiDebugAdapter<'a>(&'a [u8]); -impl<'a> fmt::Debug for AsciiDebugAdapter<'a> { +impl fmt::Debug for AsciiDebugAdapter<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_char('"')?; self.0.iter().try_for_each(|&c| match c { @@ -306,7 +306,7 @@ impl<'a> fmt::Debug for AsciiDebugAdapter<'a> { struct HexDebugAdapter<'a>(&'a [u8]); -impl<'a> fmt::Debug for HexDebugAdapter<'a> { +impl fmt::Debug for HexDebugAdapter<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("0x")?; self.0.iter().try_for_each(|x| write!(f, "{:02x}", x)) @@ -471,9 +471,9 @@ fn parse_byte(data: &[u8], offset: usize, count: usize) -> Value { fn parse_ascii(data: &[u8], offset: usize, count: usize) -> Value { // Any ASCII field can contain multiple strings [TIFF6 Image File // Directory]. - let iter = (&data[offset .. offset + count]).split(|&b| b == b'\0'); + let iter = (data[offset .. offset + count]).split(|&b| b == b'\0'); let mut v: Vec> = iter.map(|x| x.to_vec()).collect(); - if v.last().map_or(false, |x| x.len() == 0) { + if v.last().map_or(false, |x| x.is_empty()) { v.pop(); } Value::Ascii(v) diff --git a/src/writer.rs b/src/writer.rs index aca1cf5..1d8498a 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -74,7 +74,7 @@ struct Ifd<'a> { jpeg: Option<&'a [u8]>, } -impl<'a> Ifd<'a> { +impl Ifd<'_> { fn is_empty(&self) -> bool { self.tiff_fields.is_empty() && self.exif_fields.is_empty() && @@ -97,6 +97,12 @@ struct WriterState<'a> { interop_ifd_offset: u32, } +impl<'a> Default for Writer<'a> { + fn default() -> Writer<'a> { + Writer::new() + } +} + impl<'a> Writer<'a> { /// Constructs an empty `Writer`. pub fn new() -> Writer<'a> { @@ -191,7 +197,7 @@ impl<'a> Writer<'a> { ifd_num_ck.ok_or(Error::InvalidFormat("Too many IFDs"))?; if ifd_num > 0 { let next_ifd_offset = pad_and_get_offset(w)?; - let origpos = w.seek(SeekFrom::Current(0))?; + let origpos = w.stream_position()?; w.seek(SeekFrom::Start(next_ifd_offset_offset as u64))?; match little_endian { false => BigEndian::writeu32(w, next_ifd_offset)?, @@ -244,13 +250,13 @@ fn synthesize_fields(w: &mut W, ifd: &Ifd, ifd_num: In, if let Some(strips) = ifd.strips { strip_offsets = Field { tag: Tag::StripOffsets, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![0; strips.len()]), }; ws.tiff_fields.push(&strip_offsets); strip_byte_counts = Field { tag: Tag::StripByteCounts, - ifd_num: ifd_num, + ifd_num, value: Value::Long( strips.iter().map(|s| s.len() as u32).collect()), }; @@ -259,13 +265,13 @@ fn synthesize_fields(w: &mut W, ifd: &Ifd, ifd_num: In, if let Some(tiles) = ifd.tiles { tile_offsets = Field { tag: Tag::TileOffsets, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![0; tiles.len()]), }; ws.tiff_fields.push(&tile_offsets); tile_byte_counts = Field { tag: Tag::TileByteCounts, - ifd_num: ifd_num, + ifd_num, value: Value::Long( tiles.iter().map(|s| s.len() as u32).collect()), }; @@ -274,13 +280,13 @@ fn synthesize_fields(w: &mut W, ifd: &Ifd, ifd_num: In, if let Some(jpeg) = ifd.jpeg { jpeg_offset = Field { tag: Tag::JPEGInterchangeFormat, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![0]), }; ws.tiff_fields.push(&jpeg_offset); jpeg_length = Field { tag: Tag::JPEGInterchangeFormatLength, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![jpeg.len() as u32]), }; ws.tiff_fields.push(&jpeg_length); @@ -300,7 +306,7 @@ fn synthesize_fields(w: &mut W, ifd: &Ifd, ifd_num: In, ws.exif_ifd_offset = reserve_ifd(w, exif_fields_len)?; exif_in_tiff = Field { tag: Tag::ExifIFDPointer, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![ws.exif_ifd_offset]), }; ws.tiff_fields.push(&exif_in_tiff); @@ -309,7 +315,7 @@ fn synthesize_fields(w: &mut W, ifd: &Ifd, ifd_num: In, ws.gps_ifd_offset = reserve_ifd(w, gps_fields_len)?; gps_in_tiff = Field { tag: Tag::GPSInfoIFDPointer, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![ws.gps_ifd_offset]), }; ws.tiff_fields.push(&gps_in_tiff); @@ -318,7 +324,7 @@ fn synthesize_fields(w: &mut W, ifd: &Ifd, ifd_num: In, ws.interop_ifd_offset = reserve_ifd(w, interop_fields_len)?; interop_in_exif = Field { tag: Tag::InteropIFDPointer, - ifd_num: ifd_num, + ifd_num, value: Value::Long(vec![ws.interop_ifd_offset]), }; ws.exif_fields.push(&interop_in_exif); @@ -342,15 +348,15 @@ fn write_image(w: &mut W, ws: &WriterState, ifd: &Ifd) strip_offsets_offset, tile_offsets_offset, jpeg_offset) = write_ifd_and_fields::<_, E>( w, &ws.tiff_fields, ws.tiff_ifd_offset)?; - if ws.exif_fields.len() > 0 { + if !ws.exif_fields.is_empty() { write_ifd_and_fields::<_, E>( w, &ws.exif_fields, ws.exif_ifd_offset)?; } - if ws.gps_fields.len() > 0 { + if !ws.gps_fields.is_empty() { write_ifd_and_fields::<_, E>( w, &ws.gps_fields, ws.gps_ifd_offset)?; } - if ws.interop_fields.len() > 0 { + if !ws.interop_fields.is_empty() { write_ifd_and_fields::<_, E>( w, &ws.interop_fields, ws.interop_ifd_offset)?; } @@ -361,7 +367,7 @@ fn write_image(w: &mut W, ws: &WriterState, ifd: &Ifd) strip_offsets.push(get_offset(w)?); w.write_all(strip)?; } - let origpos = w.seek(SeekFrom::Current(0))?; + let origpos = w.stream_position()?; w.seek(SeekFrom::Start(strip_offsets_offset as u64))?; for ofs in strip_offsets { E::writeu32(w, ofs)?; @@ -374,7 +380,7 @@ fn write_image(w: &mut W, ws: &WriterState, ifd: &Ifd) tile_offsets.push(get_offset(w)?); w.write_all(tile)?; } - let origpos = w.seek(SeekFrom::Current(0))?; + let origpos = w.stream_position()?; w.seek(SeekFrom::Start(tile_offsets_offset as u64))?; for ofs in tile_offsets { E::writeu32(w, ofs)?; @@ -384,7 +390,7 @@ fn write_image(w: &mut W, ws: &WriterState, ifd: &Ifd) if let Some(jpeg) = ifd.jpeg { let offset = get_offset(w)?; w.write_all(jpeg)?; - let origpos = w.seek(SeekFrom::Current(0))?; + let origpos = w.stream_position()?; w.seek(SeekFrom::Start(jpeg_offset as u64))?; E::writeu32(w, offset)?; w.seek(SeekFrom::Start(origpos))?; @@ -552,7 +558,7 @@ fn compose_value(value: &Value) fn write_at(w: &mut W, buf: &[u8], offset: u32) -> io::Result<()> where W: Write + Seek { - let orig = w.seek(SeekFrom::Current(0))?; + let orig = w.stream_position()?; w.seek(SeekFrom::Start(offset as u64))?; w.write_all(buf)?; w.seek(SeekFrom::Start(orig))?; @@ -562,7 +568,7 @@ fn write_at(w: &mut W, buf: &[u8], offset: u32) // Aligns `w` to the two-byte (word) boundary and returns the new offset. fn pad_and_get_offset(w: &mut W) -> Result where W: Write + Seek { - let mut pos = w.seek(SeekFrom::Current(0))?; + let mut pos = w.stream_position()?; if pos >= (1 << 32) - 1 { return Err(Error::TooBig("Offset too large")); } @@ -575,7 +581,7 @@ fn pad_and_get_offset(w: &mut W) fn get_offset(w: &mut W) -> Result where W: Write + Seek { - let pos = w.seek(SeekFrom::Current(0))?; + let pos = w.stream_position()?; if pos as u32 as u64 != pos { return Err(Error::TooBig("Offset too large")); } diff --git a/tests/rwrcmp.rs b/tests/rwrcmp.rs index 7a5254e..4fc430a 100644 --- a/tests/rwrcmp.rs +++ b/tests/rwrcmp.rs @@ -106,7 +106,7 @@ fn rwr_compare

(path: P) where P: AsRef { if let Some(ref tiles) = tiles { writer.set_tiles(tiles, In::PRIMARY); } - if let Some(ref tn_jpeg) = tn_jpeg { + if let Some(tn_jpeg) = tn_jpeg { writer.set_jpeg(tn_jpeg, In::THUMBNAIL); } let mut out = Cursor::new(Vec::new()); @@ -160,33 +160,33 @@ fn compare_field_value(value1: &Value, value2: &Value) { } // Compare other fields strictly. match (value1, value2) { - (&Value::Ascii(ref v1), &Value::Ascii(ref v2)) => + (Value::Ascii(v1), Value::Ascii(v2)) => assert_eq!(v1, v2), - (&Value::Rational(ref v1), &Value::Rational(ref v2)) => { + (Value::Rational(v1), Value::Rational(v2)) => { assert_eq!(v1.len(), v2.len()); for (r1, r2) in v1.iter().zip(v2.iter()) { assert_eq!(r1.num, r2.num); assert_eq!(r1.denom, r2.denom); } }, - (&Value::SByte(ref v1), &Value::SByte(ref v2)) => + (Value::SByte(v1), Value::SByte(v2)) => assert_eq!(v1, v2), - (&Value::Undefined(ref v1, _), &Value::Undefined(ref v2, _)) => + (Value::Undefined(v1, _), Value::Undefined(v2, _)) => assert_eq!(v1, v2), - (&Value::SShort(ref v1), &Value::SShort(ref v2)) => + (Value::SShort(v1), Value::SShort(v2)) => assert_eq!(v1, v2), - (&Value::SLong(ref v1), &Value::SLong(ref v2)) => + (Value::SLong(v1), Value::SLong(v2)) => assert_eq!(v1, v2), - (&Value::SRational(ref v1), &Value::SRational(ref v2)) => { + (Value::SRational(v1), Value::SRational(v2)) => { assert_eq!(v1.len(), v2.len()); for (r1, r2) in v1.iter().zip(v2.iter()) { assert_eq!(r1.num, r2.num); assert_eq!(r1.denom, r2.denom); } }, - (&Value::Float(ref v1), &Value::Float(ref v2)) => + (Value::Float(v1), Value::Float(v2)) => assert_eq!(v1, v2), - (&Value::Double(ref v1), &Value::Double(ref v2)) => + (Value::Double(v1), Value::Double(v2)) => assert_eq!(v1, v2), _ => panic!("{:?} != {:?}", value1, value2), } From d714e148067c89ca821b080e8fe6b5e61bd1f045 Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sat, 30 Nov 2024 10:40:03 +0800 Subject: [PATCH 2/2] Add BigTiff support --- src/reader.rs | 13 +++- src/tag.rs | 5 ++ src/tiff.rs | 162 +++++++++++++++++++++++++++++++++++------------- src/value.rs | 93 ++++++++++++++++++--------- src/writer.rs | 16 ++++- tests/exif.bif | Bin 0 -> 387 bytes tests/rwrcmp.rs | 12 ++++ 7 files changed, 228 insertions(+), 73 deletions(-) create mode 100644 tests/exif.bif diff --git a/src/reader.rs b/src/reader.rs index 6b12ba7..d9c781e 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -121,6 +121,7 @@ impl Reader { entries: parser.entries, entry_map, little_endian: parser.little_endian, + bigtiff: parser.bigtiff, }; match parser.continue_on_error { Some(v) if !v.is_empty() => @@ -194,6 +195,8 @@ pub struct Exif { entry_map: HashMap<(In, Tag), usize>, // True if the TIFF data is little endian. little_endian: bool, + /// True if the TIFF data is in the BigTIFF format. + bigtiff: bool, } impl Exif { @@ -207,7 +210,9 @@ impl Exif { #[inline] pub fn fields(&self) -> impl ExactSizeIterator { self.entries.iter() - .map(move |e| e.ref_field(&self.buf, self.little_endian)) + .map(move |e| { + e.ref_field(&self.buf, self.little_endian) + }) } /// Returns true if the Exif data (TIFF structure) is in the @@ -217,6 +222,12 @@ impl Exif { self.little_endian } + /// Returns true if the Exif data (TIFF structure) is in BigTIFF format. + #[inline] + pub fn bigtiff(&self) -> bool { + self.bigtiff + } + /// Returns a reference to the Exif field specified by the tag /// and the IFD number. #[inline] diff --git a/src/tag.rs b/src/tag.rs index 356e3f7..b7bdc3a 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -1442,9 +1442,14 @@ fn d_default(w: &mut dyn fmt::Write, value: &Value) -> fmt::Result { Value::SRational(ref v) => d_sub_comma(w, v), Value::Float(ref v) => d_sub_comma(w, v), Value::Double(ref v) => d_sub_comma(w, v), + Value::Long8(ref v) => d_sub_comma(w, v), + Value::SLong8(ref v) => d_sub_comma(w, v), Value::Unknown(t, c, o) => write!(w, "unknown value (type={}, count={}, offset={:#x})", t, c, o), + Value::UnknownBigTiff(t, c, o) => + write!(w, "unknown value (type={}, count={}, offset={:#x})", + t, c, o), } } diff --git a/src/tiff.rs b/src/tiff.rs index c3bb547..02a3a18 100644 --- a/src/tiff.rs +++ b/src/tiff.rs @@ -39,8 +39,11 @@ use crate::value::get_type_info; const TIFF_BE: u16 = 0x4d4d; const TIFF_LE: u16 = 0x4949; const TIFF_FORTY_TWO: u16 = 0x002a; +const TIFF_FORTY_THREE: u16 = 0x002b; pub const TIFF_BE_SIG: [u8; 4] = [0x4d, 0x4d, 0x00, 0x2a]; pub const TIFF_LE_SIG: [u8; 4] = [0x49, 0x49, 0x2a, 0x00]; +pub const BIGTIFF_BE_SIG: [u8; 4] = [0x4d, 0x4d, 0x00, 0x2b]; +pub const BIGTIFF_LE_SIG: [u8; 4] = [0x49, 0x49, 0x2b, 0x00]; // Partially parsed TIFF field (IFD entry). // Value::Unknown is abused to represent a partially parsed value. @@ -63,34 +66,48 @@ impl IfdEntry { } } + /// Obtain a reference to the field. + /// Only supports regular TIFF. pub fn ref_field<'a>(&'a self, data: &[u8], le: bool) -> &'a Field { - self.parse(data, le); + self.ref_field_any(data, le, false) + } + + /// Obtain a reference to the field. + /// Supports regular TIFF and BigTiff. + pub fn ref_field_any<'a>(&'a self, data: &[u8], le: bool, bigtiff: bool) -> &'a Field { + self.parse(data, le, bigtiff); self.field.get_ref() } - fn into_field(self, data: &[u8], le: bool) -> Field { - self.parse(data, le); + fn into_field(self, data: &[u8], le: bool, bigtiff: bool) -> Field { + self.parse(data, le, bigtiff); self.field.into_inner() } - fn parse(&self, data: &[u8], le: bool) { + fn parse(&self, data: &[u8], le: bool, bigtiff: bool) { if !self.field.is_fixed() { let mut field = self.field.get_mut(); if le { - Self::parse_value::(&mut field.value, data); + Self::parse_value::(&mut field.value, data, bigtiff); } else { - Self::parse_value::(&mut field.value, data); + Self::parse_value::(&mut field.value, data, bigtiff); } } } // Converts a partially parsed value into a real one. - fn parse_value(value: &mut Value, data: &[u8]) where E: Endian { + fn parse_value(value: &mut Value, data: &[u8], bigtiff: bool) where E: Endian { match *value { Value::Unknown(typ, cnt, ofs) => { let (unitlen, parser) = get_type_info::(typ); if unitlen != 0 { - *value = parser(data, ofs as usize, cnt as usize); + *value = parser(data, ofs as usize, cnt as usize, bigtiff); + } + }, + Value::UnknownBigTiff(typ, cnt, ofs) => { + let (unitlen, parser) = get_type_info::(typ); + if unitlen != 0 { + *value = parser(data, ofs as usize, cnt as usize, bigtiff); } }, _ => panic!("value is already parsed"), @@ -153,14 +170,15 @@ impl fmt::Display for In { pub fn parse_exif(data: &[u8]) -> Result<(Vec, bool), Error> { let mut parser = Parser::new(); parser.parse(data)?; - let (entries, le) = (parser.entries, parser.little_endian); - Ok((entries.into_iter().map(|e| e.into_field(data, le)).collect(), le)) + let (entries, le, bigtiff) = (parser.entries, parser.little_endian, parser.bigtiff); + Ok((entries.into_iter().map(|e| e.into_field(data, le, bigtiff)).collect(), le)) } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Parser { pub entries: Vec, pub little_endian: bool, + pub bigtiff: bool, // `Some` to enable the option and `None` to disable it. pub continue_on_error: Option>, } @@ -170,6 +188,7 @@ impl Parser { Self { entries: Vec::new(), little_endian: false, + bigtiff: false, continue_on_error: None, } } @@ -194,11 +213,31 @@ impl Parser { fn parse_header(&mut self, data: &[u8]) -> Result<(), Error> where E: Endian { - // Parse the rest of the header (42 and the IFD offset). - if E::loadu16(data, 2) != TIFF_FORTY_TWO { - return Err(Error::InvalidFormat("Invalid forty two")); - } - let ifd_offset = E::loadu32(data, 4) as usize; + // Parse the rest of the header: + // - 42 and the IFD offset for regular TIFF. + // - 43, a constant, and the u64 IFD offset for BigTIFF. + let tiff_type = E::loadu16(data, 2); + self.bigtiff = if tiff_type == TIFF_FORTY_TWO { + false + } else if tiff_type == TIFF_FORTY_THREE { + // http://bigtiff.org/ describes the BigTIFF header additions as constants 8 and 0. + let offset_bytesize = E::loadu16(data, 4); + if offset_bytesize != 8 { + return Err(Error::InvalidFormat("Invalid BigTIFF offset byte size")); + } + let extra_field = E::loadu16(data, 6); + if extra_field != 0 { + return Err(Error::InvalidFormat("Invalid BigTIFF header")); + } + true + } else { + return Err(Error::InvalidFormat("Invalid TIFF magic number 42 or 43")); + }; + let ifd_offset = if self.bigtiff { + E::loadu64(data, 8) as usize + } else { + E::loadu32(data, 4) as usize + }; self.parse_body::(data, ifd_offset) .or_else(|e| self.check_error(e)) } @@ -226,19 +265,31 @@ impl Parser { mut offset: usize, ctx: Context, ifd_num: u16) -> Result where E: Endian { // Count (the number of the entries). - if data.len() < offset || data.len() - offset < 2 { + if !self.bigtiff && (data.len() < offset || data.len() - offset < 2) { return Err(Error::InvalidFormat("Truncated IFD count")); } - let count = E::loadu16(data, offset) as usize; - offset += 2; + let count = if self.bigtiff { + E::loadu64(data, offset) as usize + } else { + E::loadu16(data, offset) as usize + }; + if self.bigtiff { + offset += 8; + } else { + offset += 2; + } // Array of entries. for _ in 0..count { - if data.len() - offset < 12 { + if !self.bigtiff && data.len() - offset < 12 { // fixme return Err(Error::InvalidFormat("Truncated IFD")); } - let entry = Self::parse_ifd_entry::(data, offset); - offset += 12; + let entry = self.parse_ifd_entry::(data, offset); + if self.bigtiff { + offset += 20; + } else { + offset += 12; + } let (tag, value) = match entry { Ok(x) => x, Err(e) => { @@ -265,31 +316,53 @@ impl Parser { } // Offset to the next IFD. - if data.len() - offset < 4 { + if !self.bigtiff && data.len() - offset < 4 { // fixme return Err(Error::InvalidFormat("Truncated next IFD offset")); } - let next_ifd_offset = E::loadu32(data, offset); - Ok(next_ifd_offset as usize) + let next_ifd_offset = if self.bigtiff { + E::loadu64(data, offset) as usize + } else { + E::loadu32(data, offset) as usize + }; + Ok(next_ifd_offset) } - fn parse_ifd_entry(data: &[u8], offset: usize) + fn parse_ifd_entry(&self, data: &[u8], offset: usize) -> Result<(u16, Value), Error> where E: Endian { // The size of entry has been checked in parse_ifd(). let tag = E::loadu16(data, offset); let typ = E::loadu16(data, offset + 2); - let cnt = E::loadu32(data, offset + 4); - let valofs_at = offset + 8; + + let (cnt, valofs_at) = if self.bigtiff { + (E::loadu64(data, offset + 4) as usize, offset + 12) + } else { + (E::loadu32(data, offset + 4) as usize, offset + 8) + }; + let (unitlen, _parser) = get_type_info::(typ); - let vallen = unitlen.checked_mul(cnt as usize).ok_or( + let vallen = unitlen.checked_mul(cnt).ok_or( Error::InvalidFormat("Invalid entry count"))?; - let val = if vallen <= 4 { - Value::Unknown(typ, cnt, valofs_at as u32) + let max_inline_len = if self.bigtiff { 8 } else { 4 }; + let val = if vallen <= max_inline_len { + if self.bigtiff { + Value::UnknownBigTiff(typ, cnt as u64, valofs_at as u64) + } else { + Value::Unknown(typ, cnt as u32, valofs_at as u32) + } } else { - let ofs = E::loadu32(data, valofs_at) as usize; - if data.len() < ofs || data.len() - ofs < vallen { + let ofs = if self.bigtiff { + E::loadu64(data, valofs_at) as usize + } else { + E::loadu32(data, valofs_at) as usize + }; + if !self.bigtiff && (data.len() < ofs || data.len() - ofs < vallen) { // fixme return Err(Error::InvalidFormat("Truncated field value")); } - Value::Unknown(typ, cnt, ofs as u32) + if self.bigtiff { + Value::UnknownBigTiff(typ, cnt as u64, ofs as u64) + } else { + Value::Unknown(typ, cnt as u32, ofs as u32) + } }; Ok((tag, val)) } @@ -298,7 +371,7 @@ impl Parser { mut pointer: Value, ctx: Context, ifd_num: u16) -> Result<(), Error> where E: Endian { // The pointer is not yet parsed, so do it here. - IfdEntry::parse_value::(&mut pointer, data); + IfdEntry::parse_value::(&mut pointer, data, self.bigtiff); // A pointer field has type == LONG and count == 1, so the // value (IFD offset) must be embedded in the "value offset" @@ -323,7 +396,9 @@ impl Parser { } pub fn is_tiff(buf: &[u8]) -> bool { - buf.starts_with(&TIFF_BE_SIG) || buf.starts_with(&TIFF_LE_SIG) + buf.starts_with(&TIFF_BE_SIG) || buf.starts_with(&TIFF_LE_SIG) || + buf.starts_with(&BIGTIFF_BE_SIG) || buf.starts_with(&BIGTIFF_LE_SIG) + } /// A struct used to parse a DateTime field. @@ -633,29 +708,30 @@ mod tests { fn parse_ifd_entry() { // BYTE (type == 1) let data = b"\x02\x03\x00\x01\0\0\0\x04ABCD"; - assert_pat!(Parser::parse_ifd_entry::(data, 0).unwrap(), + let parser = Parser::default(); + assert_pat!(parser.parse_ifd_entry::(data, 0).unwrap(), (0x0203, Value::Unknown(1, 4, 8))); let data = b"\x02\x03\x00\x01\0\0\0\x05\0\0\0\x0cABCDE"; - assert_pat!(Parser::parse_ifd_entry::(data, 0).unwrap(), + assert_pat!(Parser::default().parse_ifd_entry::(data, 0).unwrap(), (0x0203, Value::Unknown(1, 5, 12))); let data = b"\x02\x03\x00\x01\0\0\0\x05\0\0\0\x0cABCD"; - assert_err_pat!(Parser::parse_ifd_entry::(data, 0), + assert_err_pat!(Parser::default().parse_ifd_entry::(data, 0), Error::InvalidFormat("Truncated field value")); // SHORT (type == 3) let data = b"X\x04\x05\x00\x03\0\0\0\x02ABCD"; - assert_pat!(Parser::parse_ifd_entry::(data, 1).unwrap(), + assert_pat!(Parser::default().parse_ifd_entry::(data, 1).unwrap(), (0x0405, Value::Unknown(3, 2, 9))); let data = b"X\x04\x05\x00\x03\0\0\0\x03\0\0\0\x0eXABCDEF"; - assert_pat!(Parser::parse_ifd_entry::(data, 1).unwrap(), + assert_pat!(Parser::default().parse_ifd_entry::(data, 1).unwrap(), (0x0405, Value::Unknown(3, 3, 14))); let data = b"X\x04\x05\x00\x03\0\0\0\x03\0\0\0\x0eXABCDE"; - assert_err_pat!(Parser::parse_ifd_entry::(data, 1), + assert_err_pat!(Parser::default().parse_ifd_entry::(data, 1), Error::InvalidFormat("Truncated field value")); // Really unknown let data = b"X\x01\x02\x03\x04\x05\x06\x07\x08ABCD"; - assert_pat!(Parser::parse_ifd_entry::(data, 1).unwrap(), + assert_pat!(Parser::default().parse_ifd_entry::(data, 1).unwrap(), (0x0102, Value::Unknown(0x0304, 0x05060708, 9))); } diff --git a/src/value.rs b/src/value.rs index 503636d..83cbf48 100644 --- a/src/value.rs +++ b/src/value.rs @@ -68,10 +68,19 @@ pub enum Value { /// Vector of 64-bit (double precision) floating-point numbers. /// Unused in the Exif specification. Double(Vec), + /// Vector of unsigned rationals. + /// An unsigned rational number is a pair of 64-bit unsigned integers. + Long8(Vec), + /// Vector of 64-bit signed integers. + SLong8(Vec), /// The type is unknown to this implementation. /// The associated values are the type, the count, and the /// offset of the "Value Offset" element. Unknown(u16, u32, u32), + /// The type is unknown to this implementation. + /// The associated values are the type, the count, and the + /// offset of the "Value Offset" element. + UnknownBigTiff(u16, u64, u64), } impl Value { @@ -273,10 +282,16 @@ impl fmt::Debug for Value { Self::SRational(v) => f.debug_tuple("SRational").field(v).finish(), Self::Float(v) => f.debug_tuple("Float").field(v).finish(), Self::Double(v) => f.debug_tuple("Double").field(v).finish(), + Self::Long8(v) => f.debug_tuple("Long8").field(v).finish(), + Self::SLong8(v) => f.debug_tuple("SLong8").field(v).finish(), Self::Unknown(t, c, oo) => f.debug_tuple("Unknown") .field(&format_args!("typ={}", t)) .field(&format_args!("cnt={}", c)) .field(&format_args!("ofs={:#x}", oo)).finish(), + Self::UnknownBigTiff(t, c, oo) => f.debug_tuple("UnknownBigTiff") + .field(&format_args!("typ={}", t)) + .field(&format_args!("cnt={}", c)) + .field(&format_args!("ofs={:#x}", oo)).finish(), } } } @@ -443,7 +458,7 @@ fn fmt_rational_sub(f: &mut fmt::Formatter, num: u32, denom: T) } } -type Parser = fn(&[u8], usize, usize) -> Value; +type Parser = fn(&[u8], usize, usize, bool) -> Value; // Return the length of a single value and the parser of the type. pub fn get_type_info(typecode: u16) -> (usize, Parser) where E: Endian { @@ -460,15 +475,18 @@ pub fn get_type_info(typecode: u16) -> (usize, Parser) where E: Endian { 10 => (8, parse_srational::), 11 => (4, parse_float::), 12 => (8, parse_double::), + 16 => (8, parse_long8::), + 17 => (8, parse_slong8::), + // TODO 18 => (8, parse_ifd8::), _ => (0, parse_unknown), } } -fn parse_byte(data: &[u8], offset: usize, count: usize) -> Value { +fn parse_byte(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value { Value::Byte(data[offset .. offset + count].to_vec()) } -fn parse_ascii(data: &[u8], offset: usize, count: usize) -> Value { +fn parse_ascii(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value { // Any ASCII field can contain multiple strings [TIFF6 Image File // Directory]. let iter = (data[offset .. offset + count]).split(|&b| b == b'\0'); @@ -479,16 +497,17 @@ fn parse_ascii(data: &[u8], offset: usize, count: usize) -> Value { Value::Ascii(v) } -fn parse_short(data: &[u8], offset: usize, count: usize) +fn parse_short(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); + let size_increment = if bigtiff { 4 } else { 2 }; for i in 0..count { - val.push(E::loadu16(data, offset + i * 2)); + val.push(E::loadu16(data, offset + i * size_increment)); } Value::Short(val) } -fn parse_long(data: &[u8], offset: usize, count: usize) +fn parse_long(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -497,7 +516,7 @@ fn parse_long(data: &[u8], offset: usize, count: usize) Value::Long(val) } -fn parse_rational(data: &[u8], offset: usize, count: usize) +fn parse_rational(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -509,17 +528,17 @@ fn parse_rational(data: &[u8], offset: usize, count: usize) Value::Rational(val) } -fn parse_sbyte(data: &[u8], offset: usize, count: usize) -> Value { +fn parse_sbyte(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value { let bytes = data[offset .. offset + count].iter() .map(|x| *x as i8).collect(); Value::SByte(bytes) } -fn parse_undefined(data: &[u8], offset: usize, count: usize) -> Value { +fn parse_undefined(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value { Value::Undefined(data[offset .. offset + count].to_vec(), offset as u32) } -fn parse_sshort(data: &[u8], offset: usize, count: usize) +fn parse_sshort(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -528,7 +547,7 @@ fn parse_sshort(data: &[u8], offset: usize, count: usize) Value::SShort(val) } -fn parse_slong(data: &[u8], offset: usize, count: usize) +fn parse_slong(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -537,7 +556,7 @@ fn parse_slong(data: &[u8], offset: usize, count: usize) Value::SLong(val) } -fn parse_srational(data: &[u8], offset: usize, count: usize) +fn parse_srational(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -550,7 +569,7 @@ fn parse_srational(data: &[u8], offset: usize, count: usize) } // TIFF and Rust use IEEE 754 format, so no conversion is required. -fn parse_float(data: &[u8], offset: usize, count: usize) +fn parse_float(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -560,7 +579,7 @@ fn parse_float(data: &[u8], offset: usize, count: usize) } // TIFF and Rust use IEEE 754 format, so no conversion is required. -fn parse_double(data: &[u8], offset: usize, count: usize) +fn parse_double(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value where E: Endian { let mut val = Vec::with_capacity(count); for i in 0..count { @@ -569,9 +588,27 @@ fn parse_double(data: &[u8], offset: usize, count: usize) Value::Double(val) } +fn parse_long8(data: &[u8], offset: usize, count: usize, bigtiff: bool) + -> Value where E: Endian { + let mut val = Vec::with_capacity(count); + for i in 0..count { + val.push(E::loadu64(data, offset + i * 8)); + } + Value::Long8(val) +} + +fn parse_slong8(data: &[u8], offset: usize, count: usize, bigtiff: bool) + -> Value where E: Endian { + let mut val = Vec::with_capacity(count); + for i in 0..count { + val.push(E::loadu64(data, offset + i * 8) as i64); + } + Value::SLong8(val) +} + // This is a dummy function and will never be called. #[allow(unused_variables)] -fn parse_unknown(data: &[u8], offset: usize, count: usize) -> Value { +fn parse_unknown(data: &[u8], offset: usize, count: usize, bigtiff: bool) -> Value { unreachable!() } @@ -589,7 +626,7 @@ mod tests { let (unitlen, parser) = get_type_info::(1); for &(data, ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Byte(v) => assert_eq!(v, ans), v => panic!("wrong variant {:?}", v), } @@ -610,7 +647,7 @@ mod tests { ]; let (unitlen, parser) = get_type_info::(2); for &(data, ref ans) in sets { - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Ascii(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -626,7 +663,7 @@ mod tests { let (unitlen, parser) = get_type_info::(3); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Short(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -643,7 +680,7 @@ mod tests { let (unitlen, parser) = get_type_info::(4); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Long(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -662,7 +699,7 @@ mod tests { let (unitlen, parser) = get_type_info::(5); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Rational(v) => { assert_eq!(v.len(), ans.len()); for (x, y) in v.iter().zip(ans.iter()) { @@ -683,7 +720,7 @@ mod tests { let (unitlen, parser) = get_type_info::(6); for &(data, ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::SByte(v) => assert_eq!(v, ans), v => panic!("wrong variant {:?}", v), } @@ -699,7 +736,7 @@ mod tests { let (unitlen, parser) = get_type_info::(7); for &(data, ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Undefined(v, o) => { assert_eq!(v, ans); assert_eq!(o, 1); @@ -718,7 +755,7 @@ mod tests { let (unitlen, parser) = get_type_info::(8); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::SShort(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -735,7 +772,7 @@ mod tests { let (unitlen, parser) = get_type_info::(9); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::SLong(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -754,7 +791,7 @@ mod tests { let (unitlen, parser) = get_type_info::(10); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::SRational(v) => { assert_eq!(v.len(), ans.len()); for (x, y) in v.iter().zip(ans.iter()) { @@ -776,7 +813,7 @@ mod tests { let (unitlen, parser) = get_type_info::(11); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Float(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -795,7 +832,7 @@ mod tests { let (unitlen, parser) = get_type_info::(12); for &(data, ref ans) in sets { assert!((data.len() - 1) % unitlen == 0); - match parser(data, 1, (data.len() - 1) / unitlen) { + match parser(data, 1, (data.len() - 1) / unitlen, false) { Value::Double(v) => assert_eq!(v, *ans), v => panic!("wrong variant {:?}", v), } @@ -807,7 +844,7 @@ mod tests { #[test] #[should_panic(expected = "index 5 out of range for slice of length 4")] fn short_oor() { - parse_short::(b"\x01\x02\x03\x04", 1, 2); + parse_short::(b"\x01\x02\x03\x04", 1, 2, false); } #[test] diff --git a/src/writer.rs b/src/writer.rs index 1d8498a..183eb44 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -551,7 +551,21 @@ fn compose_value(value: &Value) } Ok((12, vec.len(), buf)) }, - Value::Unknown(_, _, _) => + Value::Long8(ref vec) => { + let mut buf = Vec::new(); + for &v in vec { + E::writeu64(&mut buf, v)?; + } + Ok((12, vec.len(), buf)) + }, + Value::SLong8(ref vec) => { + let mut buf = Vec::new(); + for &v in vec { + E::writeu64(&mut buf, v as u64)?; + } + Ok((12, vec.len(), buf)) + }, + Value::Unknown(_, _, _) | Value::UnknownBigTiff(_, _, _) => Err(Error::NotSupported("Cannot write unknown field types")), } } diff --git a/tests/exif.bif b/tests/exif.bif new file mode 100644 index 0000000000000000000000000000000000000000..bfd2cbbc24875824e389e3fb0d29e02be2d9cf89 GIT binary patch literal 387 zcmebD)MnsdU|B|XzzkK#0W=9@7Bf%`t_Lc|h9t%W z72{)MVi1M0eV`fy83o{a8=zvMNP1y*iD7YzIFeqNIw?k0xOpJsKv)`!m<*B~P-_`9 zu!tD~-OK}Za{){tlwYk>oRL_Ns$``QZK$IFM8>hTP$@W7l9`s4mYI{PS5OHS3kcx{ E0L6|Dj{pDw literal 0 HcmV?d00001 diff --git a/tests/rwrcmp.rs b/tests/rwrcmp.rs index 4fc430a..85d8fdc 100644 --- a/tests/rwrcmp.rs +++ b/tests/rwrcmp.rs @@ -60,6 +60,11 @@ fn exif_tif() { rwr_compare("tests/exif.tif"); } +#[test] +fn exif_bif() { + rwr_compare("tests/exif.bif"); +} + #[test] fn exif_webp() { rwr_compare("tests/exif.webp"); @@ -133,6 +138,13 @@ fn rwr_compare

(path: P) where P: AsRef { fields2.sort_by_key(|f| (f.ifd_num, f.tag)); // Compare. + if path.display().to_string() == "tests/exif.bif" { + // TODO: Support IFD8 + eprintln!("{} fields1: {:?}", path.display(), fields1); + eprintln!("{} fields2: {:?}", path.display(), fields2); + assert_eq!(fields1.len() - 2, fields2.len()); + return; + } assert_eq!(fields1.len(), fields2.len()); for (f1, f2) in fields1.iter().zip(fields2.iter()) { assert_eq!(f1.tag, f2.tag);