Skip to main content

gamut_ifd/
types.rs

1//! The field types an IFD entry value can take.
2
3/// The type of a tag's value, stored as the 2-byte `Type` of an IFD entry (TIFF 6.0 §2).
4///
5/// The discriminants match the on-disk codes (`Byte` is `1`, `Short` is `3`, `Rational` is `5`,
6/// …). The first twelve are the TIFF 6.0 field types; the BigTIFF 64-bit additions
7/// (`Long8`/`SLong8`/`Ifd8`, codes `16`–`18`) appear only when the `bigtiff` feature is enabled,
8/// so the set stays additive and a classic-only build treats those codes as unknown.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum FieldType {
11    /// `1` — an 8-bit unsigned integer.
12    Byte,
13    /// `2` — an 8-bit byte holding a 7-bit ASCII code; NUL-terminated.
14    Ascii,
15    /// `3` — a 16-bit unsigned integer.
16    Short,
17    /// `4` — a 32-bit unsigned integer.
18    Long,
19    /// `5` — two `Long`s: a fraction's numerator then denominator.
20    Rational,
21    /// `6` — an 8-bit signed (two's-complement) integer.
22    SByte,
23    /// `7` — an 8-bit byte whose meaning depends on the field.
24    Undefined,
25    /// `8` — a 16-bit signed (two's-complement) integer.
26    SShort,
27    /// `9` — a 32-bit signed (two's-complement) integer.
28    SLong,
29    /// `10` — two `SLong`s: a signed fraction's numerator then denominator.
30    SRational,
31    /// `11` — a 32-bit IEEE single-precision float.
32    Float,
33    /// `12` — a 64-bit IEEE double-precision float.
34    Double,
35    /// `16` — a 64-bit unsigned integer (BigTIFF; `references/tiff/bigtiff.html`).
36    #[cfg(feature = "bigtiff")]
37    Long8,
38    /// `17` — a 64-bit signed (two's-complement) integer (BigTIFF).
39    #[cfg(feature = "bigtiff")]
40    SLong8,
41    /// `18` — a 64-bit unsigned IFD offset (BigTIFF).
42    #[cfg(feature = "bigtiff")]
43    Ifd8,
44}
45
46impl FieldType {
47    /// Returns the field type for an on-disk type code, or `None` for an unknown code.
48    ///
49    /// Without the `bigtiff` feature, the 64-bit BigTIFF codes (`16`–`18`) are unknown and return
50    /// `None`, so a classic-only reader skips fields of those types rather than failing.
51    #[must_use]
52    pub fn from_code(code: u16) -> Option<Self> {
53        Some(match code {
54            1 => FieldType::Byte,
55            2 => FieldType::Ascii,
56            3 => FieldType::Short,
57            4 => FieldType::Long,
58            5 => FieldType::Rational,
59            6 => FieldType::SByte,
60            7 => FieldType::Undefined,
61            8 => FieldType::SShort,
62            9 => FieldType::SLong,
63            10 => FieldType::SRational,
64            11 => FieldType::Float,
65            12 => FieldType::Double,
66            #[cfg(feature = "bigtiff")]
67            16 => FieldType::Long8,
68            #[cfg(feature = "bigtiff")]
69            17 => FieldType::SLong8,
70            #[cfg(feature = "bigtiff")]
71            18 => FieldType::Ifd8,
72            _ => return None,
73        })
74    }
75
76    /// Returns the on-disk type code.
77    #[must_use]
78    pub fn code(self) -> u16 {
79        match self {
80            FieldType::Byte => 1,
81            FieldType::Ascii => 2,
82            FieldType::Short => 3,
83            FieldType::Long => 4,
84            FieldType::Rational => 5,
85            FieldType::SByte => 6,
86            FieldType::Undefined => 7,
87            FieldType::SShort => 8,
88            FieldType::SLong => 9,
89            FieldType::SRational => 10,
90            FieldType::Float => 11,
91            FieldType::Double => 12,
92            #[cfg(feature = "bigtiff")]
93            FieldType::Long8 => 16,
94            #[cfg(feature = "bigtiff")]
95            FieldType::SLong8 => 17,
96            #[cfg(feature = "bigtiff")]
97            FieldType::Ifd8 => 18,
98        }
99    }
100
101    /// Returns the number of bytes in a single value of this type.
102    #[must_use]
103    pub fn size(self) -> usize {
104        match self {
105            FieldType::Byte | FieldType::Ascii | FieldType::SByte | FieldType::Undefined => 1,
106            FieldType::Short | FieldType::SShort => 2,
107            FieldType::Long | FieldType::SLong | FieldType::Float => 4,
108            FieldType::Rational | FieldType::SRational | FieldType::Double => 8,
109            #[cfg(feature = "bigtiff")]
110            FieldType::Long8 | FieldType::SLong8 | FieldType::Ifd8 => 8,
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn field_type_codes_round_trip() {
121        for code in 1..=12u16 {
122            let ty = FieldType::from_code(code).expect("known type");
123            assert_eq!(ty.code(), code);
124        }
125        assert_eq!(FieldType::from_code(0), None);
126        assert_eq!(FieldType::from_code(13), None);
127        assert_eq!(FieldType::Rational.size(), 8);
128        assert_eq!(FieldType::Short.size(), 2);
129    }
130
131    #[cfg(feature = "bigtiff")]
132    #[test]
133    fn bigtiff_field_type_codes_round_trip() {
134        for code in 16..=18u16 {
135            let ty = FieldType::from_code(code).expect("known BigTIFF type");
136            assert_eq!(ty.code(), code);
137        }
138        assert_eq!(FieldType::from_code(19), None);
139        // The BigTIFF 64-bit types are all 8 bytes wide.
140        assert_eq!(FieldType::Long8.size(), 8);
141        assert_eq!(FieldType::SLong8.size(), 8);
142        assert_eq!(FieldType::Ifd8.size(), 8);
143    }
144
145    /// Without `bigtiff`, codes 16–18 are unknown so a classic-only reader skips those fields.
146    #[cfg(not(feature = "bigtiff"))]
147    #[test]
148    fn bigtiff_codes_unknown_without_feature() {
149        assert_eq!(FieldType::from_code(16), None);
150        assert_eq!(FieldType::from_code(17), None);
151        assert_eq!(FieldType::from_code(18), None);
152    }
153}