diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 4bee993..217ad26 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -773,6 +773,58 @@ impl Entry { Ok(()) } + pub(crate) fn raw_value_at( + &self, + buf: &mut [u8], + bigtiff: bool, + reader: &mut EndianReader, + at: u64, + ) -> TiffResult { + if self.count == 0 { + return Ok(0); + } + + // We have no limits to handle, we do not allocate. + let value_bytes = self.type_.value_bytes(self.count)?; + + // No bytes to fill into the buffer. + if at >= value_bytes { + return Ok(0); + } + + // Case 1: the value fits in the offset field. + if value_bytes <= 4 || bigtiff && value_bytes <= 8 { + // `at < value_bytes` and `value_bytes <= 8` so casting is mathematical + let src = &self.offset[..value_bytes as usize][at as usize..]; + let len = src.len().min(buf.len()); + buf[..len].copy_from_slice(&src[..len]); + return Ok(value_bytes as usize); + } + + // Case 2: the value is stored in the reader at an offset. We will find the offset + // encoded in the entry, apply the relative start position and seek there. + self.set_reader_offset_relative(bigtiff, reader, at)?; + + let remainder = value_bytes - at; + let len = usize::try_from(remainder) + .unwrap_or(usize::MAX) + .min(buf.len()); + + let target = &mut buf[..len]; + reader.inner().read_exact(target)?; + + // Design note: in a previous draft we would consume the rest of the bytes of this value + // here (into a stack buffer if need be) to verify the stream itself. But in the end we + // have `Seek` so we better verify this by seeking over the rest of the bytes, finding if + // the stream continues that far. Even that is maybe bad if we wanted to provide a + // async-adaptor that `WouldBlock` errors to fill back a read window then the seek is + // poison to that, too. + + // So a really simple choice: The caller is responsible for handling the fact that this did + // not verify the whole value. Attempt a 1-byte read at the end of the value instead? + Ok(len) + } + // Returns `Ok(bytes)` if our value's bytes through type and count fit into `usize` and are // within the limits. Extends the buffer to that many bytes. fn buffer_with_capacity( diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index da57653..f3e254c 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -1742,6 +1742,16 @@ impl IfdDecoder<'_> { self.inner.find_tag_buf(tag, buf) } + /// Read bytes of a tag's value into a byte buffer. + pub fn find_tag_bytes( + &mut self, + tag: Tag, + buf: &mut [u8], + offset: u64, + ) -> TiffResult> { + self.inner.find_tag_raw(tag, buf, offset) + } + /// Tries to retrieve a tag and convert it to the desired unsigned type. pub fn find_tag_unsigned>(&mut self, tag: Tag) -> TiffResult> { self.find_tag(tag)? diff --git a/src/decoder/tag_reader.rs b/src/decoder/tag_reader.rs index 3f4fb7c..4386e5a 100644 --- a/src/decoder/tag_reader.rs +++ b/src/decoder/tag_reader.rs @@ -18,6 +18,9 @@ pub(crate) trait EntryDecoder { fn entry_val(&mut self, entry: &Entry) -> TiffResult; /// Fill a `ValueBuffer` by fetching the required bytes. fn entry_buf(&mut self, entry: &Entry, _: &mut ValueBuffer) -> TiffResult<()>; + /// Fill the prefix of the buffer and return the actual bytes. Start interpreting the inner + /// value at `offset`. + fn entry_raw(&mut self, entry: &Entry, _: &mut [u8], offset: u64) -> TiffResult; } impl EntryDecoder for ValueReader { @@ -28,6 +31,10 @@ impl EntryDecoder for ValueReader { fn entry_buf(&mut self, entry: &Entry, buf: &mut ValueBuffer) -> TiffResult<()> { entry.buffered_value(buf, &self.limits, self.bigtiff, &mut self.reader) } + + fn entry_raw(&mut self, entry: &Entry, buf: &mut [u8], offset: u64) -> TiffResult { + entry.raw_value_at(buf, self.bigtiff, &mut self.reader, offset) + } } impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> { @@ -58,6 +65,21 @@ impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> { Ok(Some(entry)) } + pub(crate) fn find_tag_raw( + &mut self, + tag: Tag, + buf: &mut [u8], + at: u64, + ) -> TiffResult> { + let entry = match self.ifd.get(tag) { + None => return Ok(None), + Some(entry) => entry.clone(), + }; + + let len = self.decoder.entry_raw(&entry, buf, at)?; + Ok(Some(len)) + } + pub(crate) fn require_tag(&mut self, tag: Tag) -> TiffResult { match self.find_tag(tag)? { Some(val) => Ok(val),