Read tag values into byte slice

This commit is contained in:
A. Molzer committed 2026-01-11 17:20:04 +01:00
1 parent 34bd1da99e
commit 24b008a7c3
3 files changed
+84

No files matched your search

+52
View File
@@ -773,6 +773,58 @@ impl Entry {
Ok(()) Ok(())
} }
pub(crate) fn raw_value_at<R: Read + Seek>(
&self,
buf: &mut [u8],
bigtiff: bool,
reader: &mut EndianReader<R>,
at: u64,
) -> TiffResult<usize> {
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 // 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. // within the limits. Extends the buffer to that many bytes.
fn buffer_with_capacity( fn buffer_with_capacity(
+10
View File
@@ -1742,6 +1742,16 @@ impl IfdDecoder<'_> {
self.inner.find_tag_buf(tag, buf) 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<Option<usize>> {
self.inner.find_tag_raw(tag, buf, offset)
}
/// Tries to retrieve a tag and convert it to the desired unsigned type. /// 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>> { pub fn find_tag_unsigned<T: TryFrom<u64>>(&mut self, tag: Tag) -> TiffResult<Option<T>> {
self.find_tag(tag)? self.find_tag(tag)?
+22
View File
@@ -18,6 +18,9 @@ pub(crate) trait EntryDecoder {
fn entry_val(&mut self, entry: &Entry) -> TiffResult<Value>; fn entry_val(&mut self, entry: &Entry) -> TiffResult<Value>;
/// Fill a `ValueBuffer` by fetching the required bytes. /// Fill a `ValueBuffer` by fetching the required bytes.
fn entry_buf(&mut self, entry: &Entry, _: &mut ValueBuffer) -> TiffResult<()>; 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<usize>;
} }
impl<R: Seek + Read> EntryDecoder for ValueReader<R> { impl<R: Seek + Read> EntryDecoder for ValueReader<R> {
@@ -28,6 +31,10 @@ impl<R: Seek + Read> EntryDecoder for ValueReader<R> {
fn entry_buf(&mut self, entry: &Entry, buf: &mut ValueBuffer) -> TiffResult<()> { fn entry_buf(&mut self, entry: &Entry, buf: &mut ValueBuffer) -> TiffResult<()> {
entry.buffered_value(buf, &self.limits, self.bigtiff, &mut self.reader) entry.buffered_value(buf, &self.limits, self.bigtiff, &mut self.reader)
} }
fn entry_raw(&mut self, entry: &Entry, buf: &mut [u8], offset: u64) -> TiffResult<usize> {
entry.raw_value_at(buf, self.bigtiff, &mut self.reader, offset)
}
} }
impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> { impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> {
@@ -58,6 +65,21 @@ impl<'a, R: EntryDecoder + ?Sized> TagReader<'a, R> {
Ok(Some(entry)) Ok(Some(entry))
} }
pub(crate) fn find_tag_raw(
&mut self,
tag: Tag,
buf: &mut [u8],
at: u64,
) -> TiffResult<Option<usize>> {
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<Value> { pub(crate) fn require_tag(&mut self, tag: Tag) -> TiffResult<Value> {
match self.find_tag(tag)? { match self.find_tag(tag)? {
Some(val) => Ok(val), Some(val) => Ok(val),