Skip to main content

gamut_ifd/
byte_order.rs

1//! The byte order of a TIFF/IFD stream.
2
3/// Byte order of a TIFF stream, named by the two-byte order mark at the start of the header.
4///
5/// Every scalar in the stream — IFD offsets, entry counts, and multi-byte values — is read and
6/// written in this order. Unlike most binary image formats (which fix one endianness), TIFF
7/// records its own, so a reader must thread the [`ByteOrder`] through every access.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum ByteOrder {
10    /// Little-endian (`II`, `0x4949`): least-significant byte first.
11    LittleEndian,
12    /// Big-endian (`MM`, `0x4D4D`): most-significant byte first.
13    BigEndian,
14}
15
16impl ByteOrder {
17    /// Decodes a 16-bit unsigned integer from two bytes in this order.
18    #[must_use]
19    pub fn u16(self, b: [u8; 2]) -> u16 {
20        match self {
21            ByteOrder::LittleEndian => u16::from_le_bytes(b),
22            ByteOrder::BigEndian => u16::from_be_bytes(b),
23        }
24    }
25
26    /// Decodes a 32-bit unsigned integer from four bytes in this order.
27    #[must_use]
28    pub fn u32(self, b: [u8; 4]) -> u32 {
29        match self {
30            ByteOrder::LittleEndian => u32::from_le_bytes(b),
31            ByteOrder::BigEndian => u32::from_be_bytes(b),
32        }
33    }
34
35    /// Decodes a 64-bit unsigned integer from eight bytes in this order (BigTIFF offsets/counts).
36    #[cfg(feature = "bigtiff")]
37    #[must_use]
38    pub fn u64(self, b: [u8; 8]) -> u64 {
39        match self {
40            ByteOrder::LittleEndian => u64::from_le_bytes(b),
41            ByteOrder::BigEndian => u64::from_be_bytes(b),
42        }
43    }
44
45    /// Encodes a 16-bit unsigned integer to two bytes in this order.
46    #[must_use]
47    pub fn pack_u16(self, v: u16) -> [u8; 2] {
48        match self {
49            ByteOrder::LittleEndian => v.to_le_bytes(),
50            ByteOrder::BigEndian => v.to_be_bytes(),
51        }
52    }
53
54    /// Encodes a 32-bit unsigned integer to four bytes in this order.
55    #[must_use]
56    pub fn pack_u32(self, v: u32) -> [u8; 4] {
57        match self {
58            ByteOrder::LittleEndian => v.to_le_bytes(),
59            ByteOrder::BigEndian => v.to_be_bytes(),
60        }
61    }
62
63    /// Encodes a 64-bit unsigned integer to eight bytes in this order (BigTIFF offsets/counts).
64    #[cfg(feature = "bigtiff")]
65    #[must_use]
66    pub fn pack_u64(self, v: u64) -> [u8; 8] {
67        match self {
68            ByteOrder::LittleEndian => v.to_le_bytes(),
69            ByteOrder::BigEndian => v.to_be_bytes(),
70        }
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn byte_order_roundtrips_integers() {
80        for order in [ByteOrder::LittleEndian, ByteOrder::BigEndian] {
81            assert_eq!(order.u16(order.pack_u16(0xABCD)), 0xABCD);
82            assert_eq!(order.u32(order.pack_u32(0x0123_4567)), 0x0123_4567);
83            #[cfg(feature = "bigtiff")]
84            assert_eq!(
85                order.u64(order.pack_u64(0x0123_4567_89AB_CDEF)),
86                0x0123_4567_89AB_CDEF
87            );
88        }
89        assert_eq!(ByteOrder::LittleEndian.pack_u16(0x00FF), [0xFF, 0x00]);
90        assert_eq!(ByteOrder::BigEndian.pack_u16(0x00FF), [0x00, 0xFF]);
91        #[cfg(feature = "bigtiff")]
92        {
93            assert_eq!(
94                ByteOrder::LittleEndian.pack_u64(0xFF),
95                [0xFF, 0, 0, 0, 0, 0, 0, 0]
96            );
97            assert_eq!(
98                ByteOrder::BigEndian.pack_u64(0xFF),
99                [0, 0, 0, 0, 0, 0, 0, 0xFF]
100            );
101        }
102    }
103}