diff --git a/src/decoder/ifd.rs b/src/decoder/ifd.rs index 4ae255e..4bee993 100644 --- a/src/decoder/ifd.rs +++ b/src/decoder/ifd.rs @@ -6,7 +6,7 @@ use std::mem; use std::str; use super::stream::{ByteOrder, EndianReader}; -use crate::tags::{IfdPointer, Tag, Type}; +use crate::tags::{IfdPointer, Tag, Type, ValueBuffer}; use crate::{TiffError, TiffFormatError, TiffResult}; use self::Value::{ @@ -579,7 +579,7 @@ impl Entry { // Case 4: there is more than one value, and it doesn't fit in the offset field. let mut v; - self.set_reader_offset(bo, bigtiff, reader)?; + self.set_reader_offset_relative(bigtiff, reader, 0)?; match self.type_ { Type::BYTE | Type::UNDEFINED => { @@ -737,6 +737,61 @@ impl Entry { Ok(List(v)) } + pub(crate) fn buffered_value( + &self, + buf: &mut ValueBuffer, + limits: &super::Limits, + bigtiff: bool, + reader: &mut EndianReader, + ) -> TiffResult<()> { + if self.count == 0 { + buf.assume_type(self.type_, 0, reader.byte_order); + return Ok(()); + } + + let value_bytes = self.buffer_with_capacity(buf, limits)?; + + // Case 1: the value fits in the offset field. + if value_bytes <= 4 || bigtiff && value_bytes <= 8 { + let src = &self.offset[..value_bytes]; + buf.raw_bytes_mut()[..value_bytes].copy_from_slice(src); + buf.assume_type(self.type_, self.count, reader.byte_order); + + return Ok(()); + } + + // Case 2: the value is stored in the reader at an offset. + self.set_reader_offset_relative(bigtiff, reader, 0)?; + + // In case of an error we set the type and endianess. + buf.assume_type(self.type_, 0, reader.byte_order); + let target = &mut buf.raw_bytes_mut()[..value_bytes]; + // FIXME: if the read fails we have already grown to full size, which is not great. + reader.inner().read_exact(target)?; + buf.assume_type(self.type_, self.count, reader.byte_order); + + Ok(()) + } + + // 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( + &self, + buf: &mut ValueBuffer, + limits: &super::Limits, + ) -> TiffResult { + let bytes = self.type_.value_bytes(self.count())?; + + let allowed_length = usize::try_from(bytes) + .ok() + .filter(|&n| n <= limits.decoding_buffer_size) + .ok_or(TiffError::LimitsExceeded)?; + + buf.prepare_length(allowed_length); + + Ok(allowed_length) + } + fn vec_with_capacity( value_count: u64, limits: &super::Limits, @@ -750,21 +805,32 @@ impl Entry { Ok(Vec::with_capacity(value_count)) } - fn set_reader_offset( + /// Seek to an offset within a value stored in the offset defined by this entry. + fn set_reader_offset_relative( &self, - bo: ByteOrder, bigtiff: bool, reader: &mut EndianReader, + at: u64, ) -> TiffResult<()> where R: Read + Seek, { + let bo = reader.byte_order; + let offset = if bigtiff { self.offset_field_reader(bo).read_u64()? } else { self.offset_field_reader(bo).read_u32()?.into() }; + // FIXME: `at` should be within `self.type_.value_bytes(self.count)` and that itself should + // be within the bounds of the stream. But we do not check this eagerly so this below will + // fail sometimes differently for exotic streams, depending on the method by which we read + // (at once or through multiple raw into-byte-slice reads). + let offset = offset.checked_add(at).ok_or(TiffError::FormatError( + TiffFormatError::InconsistentSizesEncountered, + ))?; + reader.goto_offset(offset)?; Ok(()) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 79b693d..da57653 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -5,7 +5,7 @@ use std::num::NonZeroUsize; use crate::tags::{ CompressionMethod, IfdPointer, PhotometricInterpretation, PlanarConfiguration, Predictor, - SampleFormat, Tag, Type, + SampleFormat, Tag, Type, ValueBuffer, }; use crate::{ bytecast, ColorType, Directory, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError, @@ -1717,12 +1717,31 @@ impl ValueReader { } impl IfdDecoder<'_> { + /// Retrieve the IFD entry for a given tag, if it exists. + /// + /// The entry contains the metadata of the value, that is its type and count from which we can + /// calculate a total byte size. + pub fn find_entry(&self, tag: Tag) -> Option { + self.inner.ifd.get(tag).cloned() + } + /// Tries to retrieve a tag. /// Return `Ok(None)` if the tag is not present. pub fn find_tag(&mut self, tag: Tag) -> TiffResult> { self.inner.find_tag(tag) } + /// Retrieve a tag and reproduce its bytes into the provided buffer. + /// + /// The buffer is unmodified if the tag is not present. + pub fn find_tag_buf( + &mut self, + tag: Tag, + buf: &mut ValueBuffer, + ) -> TiffResult> { + self.inner.find_tag_buf(tag, buf) + } + /// 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 99bba57..3f4fb7c 100644 --- a/src/decoder/tag_reader.rs +++ b/src/decoder/tag_reader.rs @@ -1,7 +1,9 @@ use std::io::{Read, Seek}; -use crate::{tags::Tag, Directory}; -use crate::{TiffError, TiffFormatError, TiffResult}; +use crate::{ + tags::{Tag, ValueBuffer}, + Directory, TiffError, TiffFormatError, TiffResult, +}; use super::ifd::{Entry, Value}; use super::ValueReader; @@ -14,12 +16,18 @@ pub(crate) struct TagReader<'lt, R: EntryDecoder + ?Sized> { pub(crate) trait EntryDecoder { /// Turn an entry into a value by fetching the required bytes in the stream. 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<()>; } impl EntryDecoder for ValueReader { fn entry_val(&mut self, entry: &Entry) -> TiffResult { entry.val(&self.limits, self.bigtiff, &mut self.reader) } + + fn entry_buf(&mut self, entry: &Entry, buf: &mut ValueBuffer) -> TiffResult<()> { + entry.buffered_value(buf, &self.limits, self.bigtiff, &mut self.reader) + } } impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> { @@ -32,6 +40,24 @@ impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> { self.decoder.entry_val(&entry).map(Some) } + /// Find a tag and fill the provided buffer with its raw data. + /// + /// Returns the entry of the tag if found. This signals that the buffer was overwritten. + /// Otherwise the buffer is preserved as-is. + pub(crate) fn find_tag_buf( + &mut self, + tag: Tag, + buf: &mut ValueBuffer, + ) -> TiffResult> { + let entry = match self.ifd.get(tag) { + None => return Ok(None), + Some(entry) => entry.clone(), + }; + + self.decoder.entry_buf(&entry, buf)?; + Ok(Some(entry)) + } + pub(crate) fn require_tag(&mut self, tag: Tag) -> TiffResult { match self.find_tag(tag)? { Some(val) => Ok(val), diff --git a/src/tags.rs b/src/tags.rs index e78dde5..45f5ed9 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -346,6 +346,10 @@ pub struct ValueBuffer { /// The type of the value. ty: Type, + /// The number of items, as `bytes` may be oversized while holding bytes that are initialized + /// but not used by any value. + count: u64, + /// The byte order of the value. byte_order: ByteOrder, } @@ -358,6 +362,7 @@ impl ValueBuffer { ValueBuffer { bytes: vec![], ty, + count: 0, byte_order: ByteOrder::native(), } } @@ -367,6 +372,7 @@ impl ValueBuffer { ValueBuffer { bytes: value.data().into_owned(), ty: ::FIELD_TYPE, + count: value.count() as u64, byte_order: ByteOrder::native(), } } @@ -381,26 +387,69 @@ impl ValueBuffer { /// The count of items in the value. pub fn count(&self) -> u64 { - (self.bytes.len() / usize::from(self.ty.byte_len())) as u64 + debug_assert!({ + self.ty + .value_bytes(self.count) + .is_ok_and(|n| n < self.bytes.len() as u64) + }); + + self.count } /// View the underlying raw bytes of this value. pub fn as_bytes(&self) -> &[u8] { - &self.bytes + &self.bytes[..self.assumed_len_from_count()] } /// View the underlying mutable raw bytes of this value. pub fn as_bytes_mut(&mut self) -> &mut [u8] { - &mut self.bytes + let len = self.assumed_len_from_count(); + &mut self.bytes[..len] } /// Change the byte order of the value representation. pub fn set_byte_order(&mut self, byte_order: ByteOrder) { + let len = self.assumed_len_from_count(); + self.byte_order - .convert(self.ty, self.bytes.as_mut_slice(), byte_order); + .convert(self.ty, &mut self.bytes[..len], byte_order); self.byte_order = byte_order; } + + /// Prepare the internal for a value `to_len` bytes long. + /// + /// Shrinks the allocation if it is far too large or extends it if it is too small. In either + /// case ensures that at least `to_len` bytes are initialized for [`Self::raw_bytes_mut`]. + pub(crate) fn prepare_length(&mut self, to_len: usize) { + if to_len > self.bytes.len() { + self.bytes.resize(to_len, 0); + } + + if self.bytes.len() < to_len / 2 { + self.bytes.truncate(to_len); + self.bytes.shrink_to_fit(); + } + } + + pub(crate) fn assume_type(&mut self, ty: Type, count: u64, bo: ByteOrder) { + debug_assert!({ + ty.value_bytes(self.count) + .is_ok_and(|n| n < self.bytes.len() as u64) + }); + + self.byte_order = bo; + self.ty = ty; + self.count = count; + } + + pub(crate) fn raw_bytes_mut(&mut self) -> &mut [u8] { + &mut self.bytes + } + + fn assumed_len_from_count(&self) -> usize { + usize::from(self.ty.byte_len()) * self.count as usize + } } /// Byte order of the TIFF file.