Merge pull request #307 from image-rs/public-byte-order

Provide a `tags::ByteOrder` public API
This commit is contained in:
Aurelia Molzer authored and GitHub committed 2025-11-05 03:40:13 +00:00
commit 34e35fb27e
4 files changed
+348 -164

No files matched your search

+218 -120
View File
@@ -376,7 +376,10 @@ impl Entry {
} }
/// Returns a mem_reader for the offset/value field /// Returns a mem_reader for the offset/value field
fn r(&self, byte_order: ByteOrder) -> EndianReader<io::Cursor<Vec<u8>>> { pub(crate) fn offset_field_reader(
&self,
byte_order: ByteOrder,
) -> EndianReader<io::Cursor<Vec<u8>>> {
EndianReader::new(io::Cursor::new(self.offset.to_vec()), byte_order) EndianReader::new(io::Cursor::new(self.offset.to_vec()), byte_order)
} }
@@ -392,43 +395,25 @@ impl Entry {
} }
let bo = reader.byte_order; let bo = reader.byte_order;
let value_bytes = self.type_.value_bytes(self.count)?;
let tag_size = match self.type_ {
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
Type::SHORT | Type::SSHORT => 2,
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
Type::LONG8
| Type::SLONG8
| Type::DOUBLE
| Type::RATIONAL
| Type::SRATIONAL
| Type::IFD8 => 8,
};
let value_bytes = match self.count.checked_mul(tag_size) {
Some(n) => n,
None => {
return Err(TiffError::LimitsExceeded);
}
};
// Case 2: there is one value. // Case 2: there is one value.
if self.count == 1 { if self.count == 1 {
// 2a: the value is 5-8 bytes and we're in BigTiff mode. // 2a: the value is 5-8 bytes and we're in BigTiff mode.
if bigtiff && value_bytes > 4 && value_bytes <= 8 { if bigtiff && value_bytes > 4 && value_bytes <= 8 {
return Ok(match self.type_ { return Ok(match self.type_ {
Type::LONG8 => UnsignedBig(self.r(bo).read_u64()?), Type::LONG8 => UnsignedBig(self.offset_field_reader(bo).read_u64()?),
Type::SLONG8 => SignedBig(self.r(bo).read_i64()?), Type::SLONG8 => SignedBig(self.offset_field_reader(bo).read_i64()?),
Type::DOUBLE => Double(self.r(bo).read_f64()?), Type::DOUBLE => Double(self.offset_field_reader(bo).read_f64()?),
Type::RATIONAL => { Type::RATIONAL => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
Rational(r.read_u32()?, r.read_u32()?) Rational(r.read_u32()?, r.read_u32()?)
} }
Type::SRATIONAL => { Type::SRATIONAL => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
SRational(r.read_i32()?, r.read_i32()?) SRational(r.read_i32()?, r.read_i32()?)
} }
Type::IFD8 => IfdBig(self.r(bo).read_u64()?), Type::IFD8 => IfdBig(self.offset_field_reader(bo).read_u64()?),
Type::BYTE Type::BYTE
| Type::SBYTE | Type::SBYTE
| Type::ASCII | Type::ASCII
@@ -447,11 +432,11 @@ impl Entry {
Type::BYTE => Byte(self.offset[0]), Type::BYTE => Byte(self.offset[0]),
Type::SBYTE => SignedByte(self.offset[0] as i8), Type::SBYTE => SignedByte(self.offset[0] as i8),
Type::UNDEFINED => Byte(self.offset[0]), Type::UNDEFINED => Byte(self.offset[0]),
Type::SHORT => Short(self.r(bo).read_u16()?), Type::SHORT => Short(self.offset_field_reader(bo).read_u16()?),
Type::SSHORT => SignedShort(self.r(bo).read_i16()?), Type::SSHORT => SignedShort(self.offset_field_reader(bo).read_i16()?),
Type::LONG => Unsigned(self.r(bo).read_u32()?), Type::LONG => Unsigned(self.offset_field_reader(bo).read_u32()?),
Type::SLONG => Signed(self.r(bo).read_i32()?), Type::SLONG => Signed(self.offset_field_reader(bo).read_i32()?),
Type::FLOAT => Float(self.r(bo).read_f32()?), Type::FLOAT => Float(self.offset_field_reader(bo).read_f32()?),
Type::ASCII => { Type::ASCII => {
if self.offset[0] == 0 { if self.offset[0] == 0 {
Ascii("".to_string()) Ascii("".to_string())
@@ -460,28 +445,28 @@ impl Entry {
} }
} }
Type::LONG8 => { Type::LONG8 => {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?;
UnsignedBig(reader.read_u64()?) UnsignedBig(reader.read_u64()?)
} }
Type::SLONG8 => { Type::SLONG8 => {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?;
SignedBig(reader.read_i64()?) SignedBig(reader.read_i64()?)
} }
Type::DOUBLE => { Type::DOUBLE => {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?;
Double(reader.read_f64()?) Double(reader.read_f64()?)
} }
Type::RATIONAL => { Type::RATIONAL => {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?;
Rational(reader.read_u32()?, reader.read_u32()?) Rational(reader.read_u32()?, reader.read_u32()?)
} }
Type::SRATIONAL => { Type::SRATIONAL => {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?;
SRational(reader.read_i32()?, reader.read_i32()?) SRational(reader.read_i32()?, reader.read_i32()?)
} }
Type::IFD => Ifd(self.r(bo).read_u32()?), Type::IFD => Ifd(self.offset_field_reader(bo).read_u32()?),
Type::IFD8 => { Type::IFD8 => {
reader.goto_offset(self.r(bo).read_u32()?.into())?; reader.goto_offset(self.offset_field_reader(bo).read_u32()?.into())?;
IfdBig(reader.read_u64()?) IfdBig(reader.read_u64()?)
} }
}); });
@@ -512,7 +497,7 @@ impl Entry {
)); ));
} }
Type::SHORT => { Type::SHORT => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
let mut v = Vec::new(); let mut v = Vec::new();
for _ in 0..self.count { for _ in 0..self.count {
v.push(Short(r.read_u16()?)); v.push(Short(r.read_u16()?));
@@ -520,7 +505,7 @@ impl Entry {
return Ok(List(v)); return Ok(List(v));
} }
Type::SSHORT => { Type::SSHORT => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
let mut v = Vec::new(); let mut v = Vec::new();
for _ in 0..self.count { for _ in 0..self.count {
v.push(SignedShort(r.read_i16()?)); v.push(SignedShort(r.read_i16()?));
@@ -528,7 +513,7 @@ impl Entry {
return Ok(List(v)); return Ok(List(v));
} }
Type::LONG => { Type::LONG => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
let mut v = Vec::new(); let mut v = Vec::new();
for _ in 0..self.count { for _ in 0..self.count {
v.push(Unsigned(r.read_u32()?)); v.push(Unsigned(r.read_u32()?));
@@ -536,7 +521,7 @@ impl Entry {
return Ok(List(v)); return Ok(List(v));
} }
Type::SLONG => { Type::SLONG => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
let mut v = Vec::new(); let mut v = Vec::new();
for _ in 0..self.count { for _ in 0..self.count {
v.push(Signed(r.read_i32()?)); v.push(Signed(r.read_i32()?));
@@ -544,7 +529,7 @@ impl Entry {
return Ok(List(v)); return Ok(List(v));
} }
Type::FLOAT => { Type::FLOAT => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
let mut v = Vec::new(); let mut v = Vec::new();
for _ in 0..self.count { for _ in 0..self.count {
v.push(Float(r.read_f32()?)); v.push(Float(r.read_f32()?));
@@ -552,7 +537,7 @@ impl Entry {
return Ok(List(v)); return Ok(List(v));
} }
Type::IFD => { Type::IFD => {
let mut r = self.r(bo); let mut r = self.offset_field_reader(bo);
let mut v = Vec::new(); let mut v = Vec::new();
for _ in 0..self.count { for _ in 0..self.count {
v.push(Ifd(r.read_u32()?)); v.push(Ifd(r.read_u32()?));
@@ -571,74 +556,149 @@ impl Entry {
} }
// Case 4: there is more than one value, and it doesn't fit in the offset field. // 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_ { match self.type_ {
// TODO check if this could give wrong results Type::BYTE | Type::UNDEFINED => {
// at a different endianess of file/computer. v = Self::vec_with_capacity(self.count, limits)?;
Type::BYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { self.decode_values(self.count, self.type_, reader, |bytes| {
let mut buf = [0; 1]; v.extend(bytes.iter().copied().map(Byte))
reader.inner().read_exact(&mut buf)?; })
Ok(Byte(buf[0])) }
}), Type::SBYTE => {
Type::SBYTE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v = Self::vec_with_capacity(self.count, limits)?;
Ok(SignedByte(reader.read_i8()?)) self.decode_values(self.count, self.type_, reader, |bytes| {
}), v.extend(bytes.iter().copied().map(|v| SignedByte(v as i8)))
Type::SHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { })
Ok(Short(reader.read_u16()?)) }
}), Type::SHORT => {
Type::SSHORT => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v = Self::vec_with_capacity(self.count, limits)?;
Ok(SignedShort(reader.read_i16()?)) self.decode_values(self.count, self.type_, reader, |bytes| {
}), v.extend(
Type::LONG => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { bytes
Ok(Unsigned(reader.read_u32()?)) .chunks_exact(2)
}), .map(|ch| Short(u16::from_ne_bytes(ch.try_into().unwrap()))),
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| { Type::SSHORT => {
Ok(Float(reader.read_f32()?)) v = Self::vec_with_capacity(self.count, limits)?;
}), self.decode_values(self.count, self.type_, reader, |bytes| {
Type::DOUBLE => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v.extend(
Ok(Double(reader.read_f64()?)) 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 => { Type::RATIONAL => {
self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v = Self::vec_with_capacity(self.count, limits)?;
Ok(Rational(reader.read_u32()?, reader.read_u32()?)) 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 => { Type::SRATIONAL => {
self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v = Self::vec_with_capacity(self.count, limits)?;
Ok(SRational(reader.read_i32()?, reader.read_i32()?)) 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| { Type::LONG8 => {
Ok(UnsignedBig(reader.read_u64()?)) v = Self::vec_with_capacity(self.count, limits)?;
}), self.decode_values(self.count, self.type_, reader, |bytes| {
Type::SLONG8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v.extend(
Ok(SignedBig(reader.read_i64()?)) bytes
}), .chunks_exact(8)
Type::IFD => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { .map(|ch| UnsignedBig(u64::from_ne_bytes(ch.try_into().unwrap()))),
Ok(Ifd(reader.read_u32()?)) )
}), })
Type::IFD8 => self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { }
Ok(IfdBig(reader.read_u64()?)) Type::SLONG8 => {
}), v = Self::vec_with_capacity(self.count, limits)?;
Type::UNDEFINED => { self.decode_values(self.count, self.type_, reader, |bytes| {
self.decode_offset(self.count, bo, bigtiff, limits, reader, |reader| { v.extend(
let mut buf = [0; 1]; bytes
reader.inner().read_exact(&mut buf)?; .chunks_exact(8)
Ok(Byte(buf[0])) .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 => { Type::ASCII => {
let n = usize::try_from(self.count)?; let n = usize::try_from(self.count)?;
if n > limits.decoding_buffer_size {
return Err(TiffError::LimitsExceeded);
}
if bigtiff { if n > limits.decoding_buffer_size {
reader.goto_offset(self.r(bo).read_u64()?)? return Err(dbg!(TiffError::LimitsExceeded));
} else {
reader.goto_offset(self.r(bo).read_u32()?.into())?
} }
let mut out = vec![0; n]; let mut out = vec![0; n];
@@ -647,43 +707,81 @@ impl Entry {
if let Some(first) = out.iter().position(|&b| b == 0) { if let Some(first) = out.iter().position(|&b| b == 0) {
out.truncate(first); out.truncate(first);
} }
Ok(Ascii(String::from_utf8(out)?))
return Ok(Ascii(String::from_utf8(out)?));
} }
} }?;
Ok(List(v))
} }
#[inline] fn vec_with_capacity(
fn decode_offset<R, F>(
&self,
value_count: u64, value_count: u64,
bo: ByteOrder,
bigtiff: bool,
limits: &super::Limits, limits: &super::Limits,
reader: &mut EndianReader<R>, ) -> Result<Vec<Value>, TiffError> {
decode_fn: F,
) -> TiffResult<Value>
where
R: Read + Seek,
F: Fn(&mut EndianReader<R>) -> TiffResult<Value>,
{
let value_count = usize::try_from(value_count)?; let value_count = usize::try_from(value_count)?;
if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() { if value_count > limits.decoding_buffer_size / mem::size_of::<Value>() {
return Err(TiffError::LimitsExceeded); 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 { let offset = if bigtiff {
self.r(bo).read_u64()? self.offset_field_reader(bo).read_u64()?
} else { } else {
self.r(bo).read_u32()?.into() self.offset_field_reader(bo).read_u32()?.into()
}; };
reader.goto_offset(offset)?; reader.goto_offset(offset)?;
for _ in 0..value_count { Ok(())
v.push(decode_fn(reader)?) }
#[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_.endian_bytes();
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_endian_bytes(cls, buffer, native);
collect(buffer);
} }
Ok(List(v))
Ok(())
} }
} }
+14 -25
View File
@@ -590,32 +590,16 @@ fn invert_colors(
/// Fix endianness. If `byte_order` matches the host, then conversion is a no-op. /// 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) { fn fix_endianness(buf: &mut [u8], byte_order: ByteOrder, bit_depth: u8) {
match byte_order { let host = ByteOrder::native();
ByteOrder::LittleEndian => match bit_depth {
0..=8 => {} let class = match bit_depth {
9..=16 => buf.chunks_exact_mut(2).for_each(|v| { 0..=8 => crate::tags::EndianBytes::One,
v.copy_from_slice(&u16::from_le_bytes((*v).try_into().unwrap()).to_ne_bytes()) 9..=16 => crate::tags::EndianBytes::Two,
}), 17..=32 => crate::tags::EndianBytes::Four,
17..=32 => buf.chunks_exact_mut(4).for_each(|v| { _ => crate::tags::EndianBytes::Eight,
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())
}),
},
}; };
host.convert_endian_bytes(class, buf, byte_order);
} }
impl<R: Read + Seek> Decoder<R> { impl<R: Read + Seek> Decoder<R> {
@@ -802,6 +786,11 @@ impl<R: Read + Seek> Decoder<R> {
} }
/// Returns the byte_order of the file. /// 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 { pub fn byte_order(&self) -> ByteOrder {
self.value_reader.reader.byte_order self.value_reader.reader.byte_order
} }
+1 -19
View File
@@ -1,14 +1,7 @@
//! All IO functionality needed for TIFF decoding //! All IO functionality needed for TIFF decoding
use std::io::{self, BufRead, BufReader, Read, Seek, Take}; use std::io::{self, BufRead, BufReader, Read, Seek, Take};
/// Byte order of the TIFF file. pub use crate::tags::ByteOrder;
#[derive(Clone, Copy, Debug)]
pub enum ByteOrder {
/// little endian byte order
LittleEndian,
/// big endian byte order
BigEndian,
}
/// Reader that is aware of the byte order. /// Reader that is aware of the byte order.
#[derive(Debug)] #[derive(Debug)]
@@ -45,17 +38,6 @@ impl<R: Read> EndianReader<R> {
}) })
} }
/// Reads an i8
#[inline(always)]
pub fn read_i8(&mut self) -> Result<i8, io::Error> {
let mut n = [0u8; 1];
self.reader.read_exact(&mut n)?;
Ok(match self.byte_order {
ByteOrder::LittleEndian => i8::from_le_bytes(n),
ByteOrder::BigEndian => i8::from_be_bytes(n),
})
}
/// Reads an i16 /// Reads an i16
#[inline(always)] #[inline(always)]
pub fn read_i16(&mut self) -> Result<i16, io::Error> { pub fn read_i16(&mut self) -> Result<i16, io::Error> {
+115
View File
@@ -193,6 +193,45 @@ pub enum Type(u16) {
} }
} }
impl Type {
pub(crate) fn byte_len(&self) -> u8 {
match *self {
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => 1,
Type::SHORT | Type::SSHORT => 2,
Type::LONG | Type::SLONG | Type::FLOAT | Type::IFD => 4,
Type::LONG8
| Type::SLONG8
| Type::DOUBLE
| Type::RATIONAL
| Type::SRATIONAL
| Type::IFD8 => 8,
}
}
pub(crate) fn value_bytes(&self, count: u64) -> Result<u64, crate::error::TiffError> {
let tag_size = u64::from(self.byte_len());
match count.checked_mul(tag_size) {
Some(n) => Ok(n),
None => Err(crate::error::TiffError::LimitsExceeded),
}
}
pub(crate) fn endian_bytes(self) -> EndianBytes {
match self {
Type::BYTE | Type::SBYTE | Type::ASCII | Type::UNDEFINED => EndianBytes::One,
Type::SHORT | Type::SSHORT => EndianBytes::Two,
Type::LONG
| Type::SLONG
| Type::FLOAT
| Type::IFD
| Type::RATIONAL
| Type::SRATIONAL => EndianBytes::Four,
Type::LONG8 | Type::SLONG8 | Type::DOUBLE | Type::IFD8 => EndianBytes::Eight,
}
}
}
tags! { tags! {
/// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html) /// See [TIFF compression tags](https://www.awaresystems.be/imaging/tiff/tifftags/compression.html)
/// for reference. /// for reference.
@@ -271,3 +310,79 @@ pub enum SampleFormat(u16) unknown(
Void = 4, Void = 4,
} }
} }
/// Byte order of the TIFF file.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ByteOrder {
/// little endian byte order
LittleEndian,
/// big endian byte order
BigEndian,
}
impl ByteOrder {
/// Get the byte order representing the running target.
///
/// The infallibility of this method represents the fact that only little and big endian
/// systems are supported by the library. No mixed endian and no other weird stuff. (Note: as
/// of Rust 1.90 this is a tautology as Rust itself only has those two kinds).
pub const fn native() -> Self {
match () {
#[cfg(target_endian = "little")]
() => ByteOrder::LittleEndian,
#[cfg(target_endian = "big")]
() => ByteOrder::BigEndian,
#[cfg(not(any(target_endian = "big", target_endian = "little")))]
() => compile_error!("Unsupported target"),
}
}
/// Given a typed buffer, convert its contents to the specified byte order in-place.
///
/// The buffer is assumed to represent an array of the given type. If the length of the buffer
/// is not divisible into an integer number of values, the behavior for the remaining bytes it
/// not specified.
pub fn convert(self, ty: Type, buffer: &mut [u8], to: ByteOrder) {
self.convert_endian_bytes(ty.endian_bytes(), buffer, to)
}
pub(crate) fn convert_endian_bytes(self, cls: EndianBytes, buffer: &mut [u8], to: ByteOrder) {
if self == to {
return;
}
// FIXME: at MSRV 1.89 or higher use `slice::as_chunks_mut`.
match cls {
EndianBytes::One => {
// No change needed
}
EndianBytes::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();
}
}
EndianBytes::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();
}
}
EndianBytes::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 EndianBytes {
One,
Two,
Four,
Eight,
}