From c073841a9d35097d3c999b0cbd7bf31407ae061f Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Thu, 10 Jul 2025 23:52:45 +0200 Subject: [PATCH] Add Decoder::restart_ifd This serves two purposes: reliably resetting the state of the decoder after kinds of failure; and moving the decoder to a SubIFD chain. --- src/decoder/mod.rs | 21 +++++++++++++++++++++ tests/subsubifds.rs | 28 +++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 63756c8..9758132 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -693,6 +693,27 @@ impl Decoder { } } + /// Start the chain of IFDs from a new location. + /// + /// This enters a new chain of image file descriptors with the indicated IFD as the new root + /// (similar to the file having its initial offset at the given IFD). After this call, + /// [`Self::seek_to_image`] works relative to the new root. + /// + /// Note that this is not atomic. If the function returns an error, the decoder is left in an + /// intermediate state where it does not point to any image. A valid state can be recovered by + /// calling [`Self::restart_ifds`] with a valid offset and a successful seek. Explicit reads + /// will still work. + pub fn restart_ifds(&mut self, offset: IfdPointer) -> TiffResult<()> { + self.ifd_offsets.clear(); + self.ifd_offsets.push(offset); + + self.next_ifd = Some(offset); + self.current_ifd = None; + + self.next_ifd()?; + Ok(()) + } + fn next_ifd(&mut self) -> TiffResult { let Some(next_ifd) = self.next_ifd.take() else { return Err(TiffError::FormatError( diff --git a/tests/subsubifds.rs b/tests/subsubifds.rs index 3fde959..0a1c234 100644 --- a/tests/subsubifds.rs +++ b/tests/subsubifds.rs @@ -1,4 +1,4 @@ -use tiff::decoder::Decoder; +use tiff::{decoder::Decoder, tags::IfdPointer}; // This file has the following IFD structure: // @@ -39,3 +39,29 @@ fn decode_seek_chain() { assert_eq!(Some(offset2), decoder.ifd_pointer()); assert!(!decoder.more_images()); } + +#[test] +fn decode_seek_recover() { + let file = File::open(TEST_IMAGE_SUBIFD).expect("Cannot open test image"); + let mut decoder = Decoder::new(file).expect("Invalid format to create decoder"); + + // Remember the offsets in the first iteration. + let offset0 = decoder.ifd_pointer().expect("First IFD pointer not found"); + decoder.next_image().unwrap(); + let offset1 = decoder.ifd_pointer().expect("Second IFD pointer not found"); + decoder.next_image().unwrap(); + let offset2 = decoder.ifd_pointer().expect("Third IFD pointer not found"); + + // oops! + let fails = decoder.restart_ifds(IfdPointer(0xdead_beef)); + assert!(fails.is_err()); + + // However we can recover by restarting our seek. + decoder.restart_ifds(offset0) + .expect("Failed to restart IFDs from first image"); + assert_eq!(Some(offset0), decoder.ifd_pointer()); + decoder.next_image().unwrap(); + assert_eq!(Some(offset1), decoder.ifd_pointer()); + decoder.next_image().unwrap(); + assert_eq!(Some(offset2), decoder.ifd_pointer()); +}