Use bulk byte order conversion for tag reads
This commit is contained in:
1 parent
19ac30ac3f
commit
99b3c03ce3
3 files changed
+260
-99
No files matched your search
+187
-74
@@ -556,74 +556,149 @@ 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)?;
|
||||
|
||||
match self.type_ {
|
||||
// TODO check if this could give wrong results
|
||||
// at a different endianess of file/computer.
|
||||
Type::BYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
let mut buf = [0; 1];
|
||||
reader.inner().read_exact(&mut buf)?;
|
||||
Ok(Byte(buf[0]))
|
||||
}),
|
||||
Type::SBYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(SignedByte(reader.read_i8()?))
|
||||
}),
|
||||
Type::SHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Short(reader.read_u16()?))
|
||||
}),
|
||||
Type::SSHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(SignedShort(reader.read_i16()?))
|
||||
}),
|
||||
Type::LONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Unsigned(reader.read_u32()?))
|
||||
}),
|
||||
Type::SLONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Signed(reader.read_i32()?))
|
||||
}),
|
||||
Type::FLOAT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Float(reader.read_f32()?))
|
||||
}),
|
||||
Type::DOUBLE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Double(reader.read_f64()?))
|
||||
}),
|
||||
Type::BYTE | Type::UNDEFINED => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.iter().copied().map(Byte))
|
||||
})
|
||||
}
|
||||
Type::SBYTE => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.iter().copied().map(|v| SignedByte(v as i8)))
|
||||
})
|
||||
}
|
||||
Type::SHORT => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(2)
|
||||
.map(|ch| Short(u16::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::SSHORT => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(2)
|
||||
.map(|ch| SignedShort(i16::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::LONG => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(4)
|
||||
.map(|ch| Unsigned(u32::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::SLONG => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(4)
|
||||
.map(|ch| Signed(i32::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::FLOAT => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(4)
|
||||
.map(|ch| Float(f32::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::DOUBLE => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(8)
|
||||
.map(|ch| Double(f64::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::RATIONAL => {
|
||||
self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Rational(reader.read_u32()?, reader.read_u32()?))
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.chunks_exact(8).map(|ch| {
|
||||
Rational(
|
||||
u32::from_ne_bytes(ch[..4].try_into().unwrap()),
|
||||
u32::from_ne_bytes(ch[4..].try_into().unwrap()),
|
||||
)
|
||||
}))
|
||||
})
|
||||
}
|
||||
Type::SRATIONAL => {
|
||||
self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(SRational(reader.read_i32()?, reader.read_i32()?))
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(bytes.chunks_exact(8).map(|ch| {
|
||||
SRational(
|
||||
i32::from_ne_bytes(ch[..4].try_into().unwrap()),
|
||||
i32::from_ne_bytes(ch[4..].try_into().unwrap()),
|
||||
)
|
||||
}))
|
||||
})
|
||||
}
|
||||
Type::LONG8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(UnsignedBig(reader.read_u64()?))
|
||||
}),
|
||||
Type::SLONG8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(SignedBig(reader.read_i64()?))
|
||||
}),
|
||||
Type::IFD => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(Ifd(reader.read_u32()?))
|
||||
}),
|
||||
Type::IFD8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
Ok(IfdBig(reader.read_u64()?))
|
||||
}),
|
||||
Type::UNDEFINED => {
|
||||
self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| {
|
||||
let mut buf = [0; 1];
|
||||
reader.inner().read_exact(&mut buf)?;
|
||||
Ok(Byte(buf[0]))
|
||||
Type::LONG8 => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(8)
|
||||
.map(|ch| UnsignedBig(u64::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::SLONG8 => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(8)
|
||||
.map(|ch| SignedBig(i64::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::IFD => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(4)
|
||||
.map(|ch| Ifd(u32::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::IFD8 => {
|
||||
v = Self::vec_with_capacity(self.count, limits)?;
|
||||
self.decode_values(self.count, self.type_, reader, |bytes| {
|
||||
v.extend(
|
||||
bytes
|
||||
.chunks_exact(8)
|
||||
.map(|ch| IfdBig(u64::from_ne_bytes(ch.try_into().unwrap()))),
|
||||
)
|
||||
})
|
||||
}
|
||||
Type::ASCII => {
|
||||
let n = usize::try_from(self.count)?;
|
||||
if n > limits.decoding_buffer_size {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
if bigtiff {
|
||||
reader.goto_offset(self.offset_field_reader(bo).read_u64()?)?
|
||||
} else {
|
||||
reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?
|
||||
if n > limits.decoding_buffer_size {
|
||||
return Err(dbg!(TiffError::LimitsExceeded));
|
||||
}
|
||||
|
||||
let mut out = vec![0; n];
|
||||
@@ -632,43 +707,81 @@ impl Entry {
|
||||
if let Some(first) = out.iter().position(|&b| b == 0) {
|
||||
out.truncate(first);
|
||||
}
|
||||
Ok(Ascii(String::from_utf8(out)?))
|
||||
}
|
||||
|
||||
return Ok(Ascii(String::from_utf8(out)?));
|
||||
}
|
||||
}?;
|
||||
|
||||
Ok(List(v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn decode_offset<R, F>(
|
||||
&self,
|
||||
fn vec_with_capacity(
|
||||
value_count: u64,
|
||||
bo: ByteOrder,
|
||||
bigtiff: bool,
|
||||
limits: &super::Limits,
|
||||
reader: &mut EndianReader<R>,
|
||||
decode_fn: F,
|
||||
) -> TiffResult<Value>
|
||||
where
|
||||
R: Read + Seek,
|
||||
F: Fn(&mut EndianReader<R>) -> TiffResult<Value>,
|
||||
{
|
||||
) -> Result<Vec<Value>, TiffError> {
|
||||
let value_count = usize::try_from(value_count)?;
|
||||
|
||||
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
|
||||
return Err(TiffError::LimitsExceeded);
|
||||
}
|
||||
|
||||
let mut v = Vec::with_capacity(value_count);
|
||||
Ok(Vec::with_capacity(value_count))
|
||||
}
|
||||
|
||||
fn set_reader_offset<R>(
|
||||
&self,
|
||||
bo: ByteOrder,
|
||||
bigtiff: bool,
|
||||
reader: &mut EndianReader<R>,
|
||||
) -> TiffResult<()>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let offset = if bigtiff {
|
||||
self.offset_field_reader(bo).read_u64()?
|
||||
} else {
|
||||
self.offset_field_reader(bo).read_u32()?.into()
|
||||
};
|
||||
|
||||
reader.goto_offset(offset)?;
|
||||
|
||||
for _ in 0..value_count {
|
||||
v.push(decode_fn(reader)?)
|
||||
Ok(())
|
||||
}
|
||||
Ok(List(v))
|
||||
|
||||
#[inline]
|
||||
fn decode_values<R, F>(
|
||||
&self,
|
||||
value_count: u64,
|
||||
type_: Type,
|
||||
reader: &mut EndianReader<R>,
|
||||
mut collect: F,
|
||||
) -> TiffResult<()>
|
||||
where
|
||||
R: Read + Seek,
|
||||
F: FnMut(&[u8]),
|
||||
{
|
||||
let mut total_bytes = type_.value_bytes(value_count)?;
|
||||
let mut buffer = [0u8; 512];
|
||||
|
||||
let buf_unit = usize::from(type_.byte_len());
|
||||
let mul_of_ty = buffer.len() / buf_unit * buf_unit;
|
||||
|
||||
let cls = type_.byte_order_class();
|
||||
let native = ByteOrder::native();
|
||||
|
||||
while total_bytes > 0 {
|
||||
// `now <= mul_of_ty < 512` so casting is mathematical
|
||||
let now = total_bytes.min(mul_of_ty as u64);
|
||||
total_bytes -= now;
|
||||
|
||||
let buffer = &mut buffer[..now as usize];
|
||||
reader.inner().read_exact(buffer)?;
|
||||
|
||||
reader.byte_order.convert_class(cls, buffer, native);
|
||||
collect(buffer);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-25
@@ -535,32 +535,16 @@ fn invert_colors(
|
||||
|
||||
/// Fix endianness. If `byte_order` matches the host, then conversion is a no-op.
|
||||
fn fix_endianness(buf: &mut [u8], byte_order: ByteOrder, bit_depth: u8) {
|
||||
match byte_order {
|
||||
ByteOrder::LittleEndian => match bit_depth {
|
||||
0..=8 => {}
|
||||
9..=16 => buf.chunks_exact_mut(2).for_each(|v| {
|
||||
v.copy_from_slice(&u16::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||
}),
|
||||
17..=32 => buf.chunks_exact_mut(4).for_each(|v| {
|
||||
v.copy_from_slice(&u32::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||
}),
|
||||
_ => buf.chunks_exact_mut(8).for_each(|v| {
|
||||
v.copy_from_slice(&u64::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||
}),
|
||||
},
|
||||
ByteOrder::BigEndian => match bit_depth {
|
||||
0..=8 => {}
|
||||
9..=16 => buf.chunks_exact_mut(2).for_each(|v| {
|
||||
v.copy_from_slice(&u16::from_be_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||
}),
|
||||
17..=32 => buf.chunks_exact_mut(4).for_each(|v| {
|
||||
v.copy_from_slice(&u32::from_be_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||
}),
|
||||
_ => buf.chunks_exact_mut(8).for_each(|v| {
|
||||
v.copy_from_slice(&u64::from_be_bytes((*v).try_into().unwrap()).to_ne_bytes())
|
||||
}),
|
||||
},
|
||||
let host = ByteOrder::native();
|
||||
|
||||
let class = match bit_depth {
|
||||
0..=8 => crate::tags::ByteOrderClass::One,
|
||||
9..=16 => crate::tags::ByteOrderClass::Two,
|
||||
17..=32 => crate::tags::ByteOrderClass::Four,
|
||||
_ => crate::tags::ByteOrderClass::Eight,
|
||||
};
|
||||
|
||||
host.convert_class(class, buf, byte_order);
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> Decoder<R> {
|
||||
@@ -747,6 +731,11 @@ impl<R: Read + Seek> Decoder<R> {
|
||||
}
|
||||
|
||||
/// Returns the byte_order of the file.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// This is only relevant to interpreting raw bytes read from tags. The image decoding methods
|
||||
/// will correct to the host byte order automatically.
|
||||
pub fn byte_order(&self) -> ByteOrder {
|
||||
self.value_reader.reader.byte_order
|
||||
}
|
||||
|
||||
+59
@@ -216,6 +216,20 @@ impl Type {
|
||||
None => Err(crate::error::TiffError::LimitsExceeded),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn byte_order_class(self) -> ByteOrderClass {
|
||||
match self {
|
||||
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => ByteOrderClass::One,
|
||||
Type::SHORT | Type::SSHORT => ByteOrderClass::Two,
|
||||
Type::LONG
|
||||
| Type::SLONG
|
||||
| Type::FLOAT
|
||||
| Type::IFD
|
||||
| Type::RATIONAL
|
||||
| Type::SRATIONAL => ByteOrderClass::Four,
|
||||
Type::LONG8 | Type::SLONG8 | Type::DOUBLE | Type::IFD8 => ByteOrderClass::Eight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tags! {
|
||||
@@ -322,4 +336,49 @@ impl ByteOrder {
|
||||
() => compile_error!("Unsupported target"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a typed buffer, convert its contents to the specified byte order in-place.
|
||||
pub fn convert(self, ty: Type, buffer: &mut [u8], to: ByteOrder) {
|
||||
self.convert_class(ty.byte_order_class(), buffer, to)
|
||||
}
|
||||
|
||||
pub(crate) fn convert_class(self, cls: ByteOrderClass, buffer: &mut [u8], to: ByteOrder) {
|
||||
if self == to {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: at MSRV 1.89 or higher use `slice::as_chunks_mut`.
|
||||
match cls {
|
||||
ByteOrderClass::One => {
|
||||
// No change needed
|
||||
}
|
||||
ByteOrderClass::Two => {
|
||||
for chunk in buffer.chunks_exact_mut(2) {
|
||||
let chunk: &mut [u8; 2] = chunk.try_into().unwrap();
|
||||
*chunk = u16::from_be_bytes(*chunk).to_le_bytes();
|
||||
}
|
||||
}
|
||||
ByteOrderClass::Four => {
|
||||
for chunk in buffer.chunks_exact_mut(4) {
|
||||
let chunk: &mut [u8; 4] = chunk.try_into().unwrap();
|
||||
*chunk = u32::from_be_bytes(*chunk).to_le_bytes();
|
||||
}
|
||||
}
|
||||
ByteOrderClass::Eight => {
|
||||
for chunk in buffer.chunks_exact_mut(8) {
|
||||
let chunk: &mut [u8; 8] = chunk.try_into().unwrap();
|
||||
*chunk = u64::from_be_bytes(*chunk).to_le_bytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of individual byte-order corrected elements.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum ByteOrderClass {
|
||||
One,
|
||||
Two,
|
||||
Four,
|
||||
Eight,
|
||||
}
|
||||
Reference in new issue
Block a user