Read raw values into ValueBuffer

This commit is contained in:
A. Molzer committed 2026-01-11 17:20:04 +01:00
1 parent cfff04c97d
commit 34bd1da99e
4 files changed
+171 -11

No files matched your search

+70 -4
View File
@@ -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<R: Read + Seek>(
&self,
buf: &mut ValueBuffer,
limits: &super::Limits,
bigtiff: bool,
reader: &mut EndianReader<R>,
) -> 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<usize> {
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<R>(
/// Seek to an offset within a value stored in the offset defined by this entry.
fn set_reader_offset_relative<R>(
&self,
bo: ByteOrder,
bigtiff: bool,
reader: &mut EndianReader<R>,
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(())
+20 -1
View File
@@ -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<R: Seek + Read> ValueReader<R> {
}
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<ifd::Entry> {
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<Option<ifd::Value>> {
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<Option<ifd::Entry>> {
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<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.find_tag(tag)?
+28 -2
View File
@@ -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<Value>;
/// Fill a `ValueBuffer` by fetching the required bytes.
fn entry_buf(&mut self, entry: &Entry, _: &mut ValueBuffer) -> TiffResult<()>;
}
impl<R: Seek + Read> EntryDecoder for ValueReader<R> {
fn entry_val(&mut self, entry: &Entry) -> TiffResult<Value> {
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<Option<Entry>> {
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<Value> {
match self.find_tag(tag)? {
Some(val) => Ok(val),
+53 -4
View File
@@ -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: <T as TiffValue>::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.