Skip to main content

gamut_ifd/
value.rs

1//! The decoded value of an IFD entry.
2
3use gamut_core::{Error, Result};
4
5use crate::byte_order::ByteOrder;
6use crate::types::FieldType;
7
8/// The decoded value(s) of one IFD entry, one variant per [`crate::FieldType`].
9///
10/// A TIFF entry always stores a `count` of values of a single type; even a scalar is a 1-element
11/// vector here. On disk the values sit inline in the entry's value/offset field when they fit, or
12/// at a file offset otherwise — a distinction the reader/writer resolve, leaving this type purely
13/// the logical value. The BigTIFF 64-bit variants (`Long8`/`SLong8`/`Ifd8`) appear only when the
14/// `bigtiff` feature is enabled.
15#[derive(Debug, Clone, PartialEq)]
16pub enum Value {
17    /// `BYTE` — unsigned 8-bit integers.
18    Byte(Vec<u8>),
19    /// `ASCII` — a NUL-terminated 7-bit ASCII string (the terminator is not stored here).
20    Ascii(String),
21    /// `SHORT` — unsigned 16-bit integers.
22    Short(Vec<u16>),
23    /// `LONG` — unsigned 32-bit integers.
24    Long(Vec<u32>),
25    /// `RATIONAL` — unsigned fractions as (numerator, denominator) pairs.
26    Rational(Vec<(u32, u32)>),
27    /// `SBYTE` — signed 8-bit integers.
28    SByte(Vec<i8>),
29    /// `UNDEFINED` — raw bytes whose interpretation depends on the field.
30    Undefined(Vec<u8>),
31    /// `SSHORT` — signed 16-bit integers.
32    SShort(Vec<i16>),
33    /// `SLONG` — signed 32-bit integers.
34    SLong(Vec<i32>),
35    /// `SRATIONAL` — signed fractions as (numerator, denominator) pairs.
36    SRational(Vec<(i32, i32)>),
37    /// `FLOAT` — IEEE single-precision floats.
38    Float(Vec<f32>),
39    /// `DOUBLE` — IEEE double-precision floats.
40    Double(Vec<f64>),
41    /// `LONG8` — BigTIFF 64-bit unsigned integers.
42    #[cfg(feature = "bigtiff")]
43    Long8(Vec<u64>),
44    /// `SLONG8` — BigTIFF 64-bit signed integers.
45    #[cfg(feature = "bigtiff")]
46    SLong8(Vec<i64>),
47    /// `IFD8` — BigTIFF 64-bit IFD offsets.
48    #[cfg(feature = "bigtiff")]
49    Ifd8(Vec<u64>),
50}
51
52impl Value {
53    /// The field type of this value.
54    #[must_use]
55    pub fn field_type(&self) -> FieldType {
56        match self {
57            Value::Byte(_) => FieldType::Byte,
58            Value::Ascii(_) => FieldType::Ascii,
59            Value::Short(_) => FieldType::Short,
60            Value::Long(_) => FieldType::Long,
61            Value::Rational(_) => FieldType::Rational,
62            Value::SByte(_) => FieldType::SByte,
63            Value::Undefined(_) => FieldType::Undefined,
64            Value::SShort(_) => FieldType::SShort,
65            Value::SLong(_) => FieldType::SLong,
66            Value::SRational(_) => FieldType::SRational,
67            Value::Float(_) => FieldType::Float,
68            Value::Double(_) => FieldType::Double,
69            #[cfg(feature = "bigtiff")]
70            Value::Long8(_) => FieldType::Long8,
71            #[cfg(feature = "bigtiff")]
72            Value::SLong8(_) => FieldType::SLong8,
73            #[cfg(feature = "bigtiff")]
74            Value::Ifd8(_) => FieldType::Ifd8,
75        }
76    }
77
78    /// The `Count` of this value: the number of elements, or for `ASCII` the number of bytes
79    /// including the terminating NUL.
80    #[must_use]
81    pub fn count(&self) -> usize {
82        match self {
83            Value::Byte(v) | Value::Undefined(v) => v.len(),
84            Value::Ascii(s) => s.len() + 1,
85            Value::Short(v) => v.len(),
86            Value::Long(v) => v.len(),
87            Value::Rational(v) => v.len(),
88            Value::SByte(v) => v.len(),
89            Value::SShort(v) => v.len(),
90            Value::SLong(v) => v.len(),
91            Value::SRational(v) => v.len(),
92            Value::Float(v) => v.len(),
93            Value::Double(v) => v.len(),
94            #[cfg(feature = "bigtiff")]
95            Value::Long8(v) | Value::Ifd8(v) => v.len(),
96            #[cfg(feature = "bigtiff")]
97            Value::SLong8(v) => v.len(),
98        }
99    }
100
101    /// The number of bytes this value occupies on disk (`count * type size`).
102    #[must_use]
103    pub fn byte_len(&self) -> usize {
104        self.count() * self.field_type().size()
105    }
106
107    /// Coerces a single unsigned-integer value (`BYTE`, `SHORT`, `LONG`, or — with `bigtiff` —
108    /// `LONG8`/`IFD8`) to `u32`.
109    ///
110    /// TIFF readers accept any of these types for an integer field (TIFF 6.0 §2); returns `None`
111    /// if the value is not a single unsigned integer or a `LONG8`/`IFD8` exceeds `u32::MAX` (only
112    /// possible past the 4 GiB classic-TIFF limit, which an in-memory decode cannot reach anyway).
113    #[must_use]
114    pub fn as_u32(&self) -> Option<u32> {
115        match self {
116            Value::Byte(v) if v.len() == 1 => Some(u32::from(v[0])),
117            Value::Short(v) if v.len() == 1 => Some(u32::from(v[0])),
118            Value::Long(v) if v.len() == 1 => Some(v[0]),
119            #[cfg(feature = "bigtiff")]
120            Value::Long8(v) | Value::Ifd8(v) if v.len() == 1 => u32::try_from(v[0]).ok(),
121            _ => None,
122        }
123    }
124
125    /// Coerces an array of unsigned integers (`BYTE`, `SHORT`, `LONG`, or — with `bigtiff` —
126    /// `LONG8`/`IFD8`) to `Vec<u32>`.
127    ///
128    /// Returns `None` for any other type, or if a `LONG8`/`IFD8` element exceeds `u32::MAX`. This
129    /// lets a decoder read BigTIFF `StripOffsets`/`StripByteCounts`, which libtiff writes as
130    /// `LONG8`.
131    #[must_use]
132    pub fn as_u32_vec(&self) -> Option<Vec<u32>> {
133        match self {
134            Value::Byte(v) => Some(v.iter().map(|&x| u32::from(x)).collect()),
135            Value::Short(v) => Some(v.iter().map(|&x| u32::from(x)).collect()),
136            Value::Long(v) => Some(v.clone()),
137            #[cfg(feature = "bigtiff")]
138            Value::Long8(v) | Value::Ifd8(v) => v.iter().map(|&x| u32::try_from(x).ok()).collect(),
139            _ => None,
140        }
141    }
142
143    /// Serialises the value's elements to bytes in `order` (without any inline/offset padding).
144    #[must_use]
145    pub fn encode(&self, order: ByteOrder) -> Vec<u8> {
146        let mut out = Vec::with_capacity(self.byte_len());
147        match self {
148            Value::Byte(v) | Value::Undefined(v) => out.extend_from_slice(v),
149            Value::Ascii(s) => {
150                out.extend_from_slice(s.as_bytes());
151                out.push(0);
152            }
153            Value::SByte(v) => out.extend(v.iter().map(|&x| x as u8)),
154            Value::Short(v) => {
155                for &x in v {
156                    out.extend_from_slice(&order.pack_u16(x));
157                }
158            }
159            Value::SShort(v) => {
160                for &x in v {
161                    out.extend_from_slice(&order.pack_u16(x as u16));
162                }
163            }
164            Value::Long(v) => {
165                for &x in v {
166                    out.extend_from_slice(&order.pack_u32(x));
167                }
168            }
169            Value::SLong(v) => {
170                for &x in v {
171                    out.extend_from_slice(&order.pack_u32(x as u32));
172                }
173            }
174            Value::Float(v) => {
175                for &x in v {
176                    out.extend_from_slice(&order.pack_u32(x.to_bits()));
177                }
178            }
179            Value::Rational(v) => {
180                for &(n, d) in v {
181                    out.extend_from_slice(&order.pack_u32(n));
182                    out.extend_from_slice(&order.pack_u32(d));
183                }
184            }
185            Value::SRational(v) => {
186                for &(n, d) in v {
187                    out.extend_from_slice(&order.pack_u32(n as u32));
188                    out.extend_from_slice(&order.pack_u32(d as u32));
189                }
190            }
191            Value::Double(v) => {
192                for &x in v {
193                    let b = x.to_bits();
194                    let lo = order.pack_u32(b as u32);
195                    let hi = order.pack_u32((b >> 32) as u32);
196                    match order {
197                        ByteOrder::LittleEndian => {
198                            out.extend_from_slice(&lo);
199                            out.extend_from_slice(&hi);
200                        }
201                        ByteOrder::BigEndian => {
202                            out.extend_from_slice(&hi);
203                            out.extend_from_slice(&lo);
204                        }
205                    }
206                }
207            }
208            #[cfg(feature = "bigtiff")]
209            Value::Long8(v) | Value::Ifd8(v) => {
210                for &x in v {
211                    out.extend_from_slice(&order.pack_u64(x));
212                }
213            }
214            #[cfg(feature = "bigtiff")]
215            Value::SLong8(v) => {
216                for &x in v {
217                    out.extend_from_slice(&order.pack_u64(x as u64));
218                }
219            }
220        }
221        out
222    }
223
224    /// Parses `count` values of `ty` from `bytes` (which must hold at least `count * ty.size()`
225    /// bytes) in `order`.
226    ///
227    /// # Errors
228    ///
229    /// Returns [`Error::InvalidInput`] if `bytes` is too short for the declared count.
230    pub fn decode(ty: FieldType, count: usize, bytes: &[u8], order: ByteOrder) -> Result<Value> {
231        let need = count
232            .checked_mul(ty.size())
233            .ok_or(Error::InvalidInput("TIFF: field length overflow"))?;
234        let bytes = bytes
235            .get(..need)
236            .ok_or(Error::InvalidInput("TIFF: field value out of bounds"))?;
237        let u16s =
238            |b: &[u8]| -> Vec<u16> { b.chunks_exact(2).map(|c| order.u16([c[0], c[1]])).collect() };
239        let u32s = |b: &[u8]| -> Vec<u32> {
240            b.chunks_exact(4)
241                .map(|c| order.u32([c[0], c[1], c[2], c[3]]))
242                .collect()
243        };
244        #[cfg(feature = "bigtiff")]
245        let u64s = |b: &[u8]| -> Vec<u64> {
246            b.chunks_exact(8)
247                .map(|c| order.u64([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]))
248                .collect()
249        };
250        Ok(match ty {
251            FieldType::Byte => Value::Byte(bytes.to_vec()),
252            FieldType::Undefined => Value::Undefined(bytes.to_vec()),
253            FieldType::SByte => Value::SByte(bytes.iter().map(|&x| x as i8).collect()),
254            FieldType::Ascii => {
255                let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
256                let s = core::str::from_utf8(&bytes[..end])
257                    .map_err(|_| Error::InvalidInput("TIFF: non-UTF-8 ASCII field"))?;
258                Value::Ascii(s.to_owned())
259            }
260            FieldType::Short => Value::Short(u16s(bytes)),
261            FieldType::SShort => Value::SShort(u16s(bytes).into_iter().map(|x| x as i16).collect()),
262            FieldType::Long => Value::Long(u32s(bytes)),
263            FieldType::SLong => Value::SLong(u32s(bytes).into_iter().map(|x| x as i32).collect()),
264            FieldType::Float => Value::Float(u32s(bytes).into_iter().map(f32::from_bits).collect()),
265            FieldType::Rational => {
266                let w = u32s(bytes);
267                Value::Rational(w.chunks_exact(2).map(|c| (c[0], c[1])).collect())
268            }
269            FieldType::SRational => {
270                let w = u32s(bytes);
271                Value::SRational(
272                    w.chunks_exact(2)
273                        .map(|c| (c[0] as i32, c[1] as i32))
274                        .collect(),
275                )
276            }
277            FieldType::Double => {
278                let mut v = Vec::with_capacity(count);
279                for c in bytes.chunks_exact(8) {
280                    let (a, b) = (
281                        order.u32([c[0], c[1], c[2], c[3]]),
282                        order.u32([c[4], c[5], c[6], c[7]]),
283                    );
284                    let bits = match order {
285                        ByteOrder::LittleEndian => u64::from(a) | (u64::from(b) << 32),
286                        ByteOrder::BigEndian => u64::from(b) | (u64::from(a) << 32),
287                    };
288                    v.push(f64::from_bits(bits));
289                }
290                Value::Double(v)
291            }
292            #[cfg(feature = "bigtiff")]
293            FieldType::Long8 => Value::Long8(u64s(bytes)),
294            #[cfg(feature = "bigtiff")]
295            FieldType::Ifd8 => Value::Ifd8(u64s(bytes)),
296            #[cfg(feature = "bigtiff")]
297            FieldType::SLong8 => Value::SLong8(u64s(bytes).into_iter().map(|x| x as i64).collect()),
298        })
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305
306    fn value_roundtrip(value: Value, order: ByteOrder) {
307        let bytes = value.encode(order);
308        let decoded =
309            Value::decode(value.field_type(), value.count(), &bytes, order).expect("decode");
310        assert_eq!(decoded, value);
311    }
312
313    #[test]
314    fn values_roundtrip_in_both_orders() {
315        for order in [ByteOrder::LittleEndian, ByteOrder::BigEndian] {
316            value_roundtrip(Value::Byte(vec![1, 2, 3]), order);
317            value_roundtrip(Value::Ascii("gamut".to_owned()), order);
318            value_roundtrip(Value::Short(vec![256, 257, 0xFFFF]), order);
319            value_roundtrip(Value::Long(vec![0xDEAD_BEEF, 7]), order);
320            value_roundtrip(Value::Rational(vec![(300, 1), (72, 1)]), order);
321            value_roundtrip(Value::SByte(vec![-1, 2, -128]), order);
322            value_roundtrip(Value::SShort(vec![-1, 30000]), order);
323            value_roundtrip(Value::SLong(vec![-1, i32::MIN]), order);
324            value_roundtrip(Value::SRational(vec![(-1, 2)]), order);
325            value_roundtrip(Value::Float(vec![1.5, -0.25]), order);
326            value_roundtrip(Value::Double(vec![1.5, -0.0625]), order);
327            value_roundtrip(Value::Undefined(vec![0, 255, 7]), order);
328            #[cfg(feature = "bigtiff")]
329            {
330                value_roundtrip(
331                    Value::Long8(vec![0x0123_4567_89AB_CDEF, 0, u64::MAX]),
332                    order,
333                );
334                value_roundtrip(Value::SLong8(vec![-1, i64::MIN, 42]), order);
335                value_roundtrip(Value::Ifd8(vec![16, 0x1_0000_0000]), order);
336            }
337        }
338    }
339
340    #[test]
341    fn integer_coercion_accepts_byte_short_long() {
342        assert_eq!(Value::Byte(vec![5]).as_u32(), Some(5));
343        assert_eq!(Value::Short(vec![300]).as_u32(), Some(300));
344        assert_eq!(Value::Long(vec![70000]).as_u32(), Some(70000));
345        assert_eq!(Value::Short(vec![1, 2]).as_u32(), None);
346        assert_eq!(Value::Ascii("x".into()).as_u32(), None);
347        assert_eq!(
348            Value::Short(vec![1, 2, 3]).as_u32_vec(),
349            Some(vec![1, 2, 3])
350        );
351    }
352
353    /// BigTIFF `LONG8`/`IFD8` coerce to `u32` when in range, so a decoder reads 64-bit offsets;
354    /// out-of-range values fail cleanly rather than truncating.
355    #[cfg(feature = "bigtiff")]
356    #[test]
357    fn integer_coercion_accepts_bigtiff_64bit() {
358        assert_eq!(Value::Long8(vec![70000]).as_u32(), Some(70000));
359        assert_eq!(Value::Ifd8(vec![8, 1024]).as_u32_vec(), Some(vec![8, 1024]));
360        assert_eq!(Value::Long8(vec![0x1_0000_0000]).as_u32(), None);
361        assert_eq!(Value::Long8(vec![1, 0x1_0000_0000]).as_u32_vec(), None);
362    }
363
364    #[test]
365    fn decode_rejects_truncated_value() {
366        // A LONG needs 4 bytes; only 2 are supplied.
367        assert!(Value::decode(FieldType::Long, 1, &[0, 0], ByteOrder::LittleEndian).is_err());
368    }
369}