tiff_forge/ifd/
values.rs

1//! The values contained or pointed at by an IFD Field.
2//!
3//! There are three groups of [`FieldValues`]: [`TiffTypeValues`],
4//! [`Offsets`] and [`OffsetsToIfds`]. The first represents a list
5//! of values of any given [`TiffType`]. The second represents a
6//! list of [`LONG`] values, each pointing to a specific [`Datablock`].
7//! The third represents a list of [`IFD`] values, each pointing to
8//! an [`Ifd`].
9//!
10//! [`FieldValues`]: trait.FieldValues.html
11//! [`TiffTypeValues`]: struct.TiffTypeValues.html
12//! [`Offsets`]: struct.Offsets.html
13//! [`OffsetsToIfds`]: struct.OffsetsToIfds.html
14//! [`TiffType`]: ../types/trait.TiffType.html
15//! [`LONG`]: ../types/struct.LONG.html
16//! [`IFD`]: ../types/struct.IFD.html
17//! [`Datablock`]: ../../write/trait.Datablock.html
18
19use std::io;
20use std::marker::PhantomData;
21
22use crate::ifd::types::{BIGIFD, IFD, LONG, LONG8, TiffType};
23use crate::ifd::{AllocatedIfdChain, IfdChain};
24use crate::write::{Cursor, Datablock, EndianFile, OffsetSize};
25
26/// The values contained or pointed at by an IFD Field.
27///
28/// Generic over `O: OffsetSize` to support both TIFF (u32) and BigTIFF (u64).
29///
30/// There are three groups of `FieldValues`: [`TiffTypeValues`],
31/// [`Offsets`] and [`OffsetsToIfds`]. The first represents a list
32/// of values of any given [`TiffType`]. The second represents a
33/// list of [`LONG`] values, each pointing to a specific [`Datablock`].
34/// The third represents a list of [`IFD`] values, each pointing to
35/// an [`Ifd`].
36///
37/// This trait is sealed. It is not possible to implement it outside
38/// this crate.
39///
40/// [`TiffTypeValues`]: struct.TiffTypeValues.html
41/// [`Offsets`]: struct.Offsets.html
42/// [`OffsetsToIfds`]: struct.OffsetsToIfds.html
43/// [`TiffType`]: ../types/trait.TiffType.html
44/// [`LONG`]: ../types/struct.LONG.html
45/// [`IFD`]: ../types/struct.IFD.html
46/// [`Datablock`]: ../../write/trait.Datablock.html
47pub trait FieldValues<O: OffsetSize = u32>: private::Sealed<O> {
48    /// The number of values the field contains.
49    #[doc(hidden)]
50    fn count(&self) -> O;
51    /// The sum of the size of every value in this field.
52    ///
53    /// This doesn't include `Datablocks` owned by this field.
54    #[doc(hidden)]
55    fn size(&self) -> O;
56    /// Allocates the needed space in the given `Cursor`, transforming into
57    /// an `AllocatedFieldValues`.
58    #[doc(hidden)]
59    fn allocate(self: Box<Self>, c: &mut Cursor<O>) -> Box<dyn AllocatedFieldValues<O>>;
60}
61
62/// Allocated form of `FieldValues`
63///
64/// Generic over `O: OffsetSize` to support both TIFF (u32) and BigTIFF (u64).
65#[doc(hidden)]
66pub trait AllocatedFieldValues<O: OffsetSize = u32> {
67    /// The number of values the field contains.
68    fn count(&self) -> O;
69    /// The sum of the size of every value in this field.
70    ///
71    /// This doesn't include `Datablocks` owned by this field.
72    fn size(&self) -> O;
73    /// The offset to the first value (counting from the beginning of the file)
74    /// if the values don't fit in the IFD entry (in other words, if `size()` is
75    /// bigger than the inline threshold).
76    fn position(&self) -> Option<O>;
77    /// The TIFF 16-bit code that identifies the type of the values of the field.
78    fn type_id(&self) -> u16;
79    /// Write the values to the given `EndianFile`, as well as any other data
80    /// they point to.
81    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()>;
82}
83
84/// Seals FieldValues, so that it can only be implemented inside
85/// the crate. There are only three types of FieldValues:
86/// `Offsets` to datablocks, `OffsetsToIfds` and `TiffTypeValues`.
87mod private {
88    use crate::write::OffsetSize;
89
90    pub trait Sealed<O: OffsetSize> {}
91    impl<T: super::Datablock, O: OffsetSize> Sealed<O> for super::Offsets<T, O> {}
92    impl<T, O: OffsetSize> Sealed<O> for super::TiffTypeValues<T, O> {}
93    impl<O: OffsetSize> Sealed<O> for super::OffsetsToIfds<O> {}
94}
95
96/// A list of [`LONG`] values, each pointing to a specific
97/// [`Datablock`].
98///
99/// This structure owns the list of Datablocks instead, so the user
100/// doesn't have to deal with the offsets in the file. It is responsible
101/// for writing both the offsets and the blocks of data.
102///
103/// Generic over `O: OffsetSize` to support both TIFF (u32) and BigTIFF (u64).
104///
105/// [`LONG`]: ../types/struct.LONG.html
106/// [`Datablock`]: ../../write/trait.Datablock.html
107pub struct Offsets<T: Datablock, O: OffsetSize = u32> {
108    pub data: Vec<T>,
109    _phantom: PhantomData<O>,
110}
111
112impl<T: Datablock + 'static, O: OffsetSize> Offsets<T, O> {
113    /// Creates a new `Offsets` instance from a vector of [`Datablock`]s.
114    ///
115    /// [`Datablock`]: ../../write/trait.Datablock.html
116    pub fn new(datablocks: Vec<T>) -> Self {
117        Offsets {
118            data: datablocks,
119            _phantom: PhantomData,
120        }
121    }
122
123    /// Creates a new `Offsets` instance from a single [`Datablock`].
124    ///
125    /// [`Datablock`]: ../../write/trait.Datablock.html
126    pub fn single(datablock: T) -> Self {
127        Offsets::new(vec![datablock])
128    }
129}
130
131impl<T: Datablock + 'static> Offsets<T, u32> {
132    /// Creates a BigTIFF-compatible `Offsets` instance from a vector of [`Datablock`]s.
133    ///
134    /// [`Datablock`]: ../../write/trait.Datablock.html
135    pub fn big_new(datablocks: Vec<T>) -> Offsets<T, u64> {
136        Offsets {
137            data: datablocks,
138            _phantom: PhantomData,
139        }
140    }
141
142    /// Creates a BigTIFF-compatible `Offsets` instance from a single [`Datablock`].
143    ///
144    /// [`Datablock`]: ../../write/trait.Datablock.html
145    pub fn big_single(datablock: T) -> Offsets<T, u64> {
146        Offsets::big_new(vec![datablock])
147    }
148}
149
150impl<T: Datablock + 'static> FieldValues<u32> for Offsets<T, u32> {
151    fn count(&self) -> u32 {
152        self.data.len() as u32
153    }
154
155    fn size(&self) -> u32 {
156        <LONG as TiffType<u32>>::size() * self.count()
157    }
158
159    fn allocate(self: Box<Self>, c: &mut Cursor<u32>) -> Box<dyn AllocatedFieldValues<u32>> {
160        let position = Some(c.allocated_bytes());
161        if self.data.len() == 1 {
162            let offsets = Vec::new();
163            let block_size = self.data.get(0).unwrap().size() as u32;
164
165            c.allocate(if block_size % 2 == 0 {
166                block_size
167            } else {
168                block_size + 1
169            });
170
171            Box::new(AllocatedOffsets {
172                position,
173                offsets,
174                data: self.data,
175                _phantom: PhantomData,
176            })
177        } else {
178            c.allocate(self.size());
179            let mut offsets = Vec::with_capacity(self.data.len());
180
181            for block in self.data.iter() {
182                offsets.push(LONG(c.allocated_bytes()));
183                let block_size = block.size() as u32;
184                c.allocate(if block_size % 2 == 0 {
185                    block_size
186                } else {
187                    block_size + 1
188                });
189            }
190
191            Box::new(AllocatedOffsets {
192                position,
193                offsets,
194                data: self.data,
195                _phantom: PhantomData,
196            })
197        }
198    }
199}
200
201impl<T: Datablock + 'static> FieldValues<u64> for Offsets<T, u64> {
202    fn count(&self) -> u64 {
203        self.data.len() as u64
204    }
205
206    fn size(&self) -> u64 {
207        <LONG8 as TiffType<u64>>::size() * self.count()
208    }
209
210    fn allocate(self: Box<Self>, c: &mut Cursor<u64>) -> Box<dyn AllocatedFieldValues<u64>> {
211        let position = Some(c.allocated_bytes());
212        if self.data.len() == 1 {
213            let offsets = Vec::new();
214            let block_size = self.data.get(0).unwrap().size() as u64;
215
216            c.allocate(if block_size % 2 == 0 {
217                block_size
218            } else {
219                block_size + 1
220            });
221
222            Box::new(AllocatedOffsets64 {
223                position,
224                offsets,
225                data: self.data,
226            })
227        } else {
228            c.allocate(self.size());
229            let mut offsets = Vec::with_capacity(self.data.len());
230
231            for block in self.data.iter() {
232                offsets.push(LONG8(c.allocated_bytes()));
233                let block_size = block.size() as u64;
234                c.allocate(if block_size % 2 == 0 {
235                    block_size
236                } else {
237                    block_size + 1
238                });
239            }
240
241            Box::new(AllocatedOffsets64 {
242                position,
243                offsets,
244                data: self.data,
245            })
246        }
247    }
248}
249
250/// Allocated form of `Offsets` for u32
251struct AllocatedOffsets<T: Datablock, O: OffsetSize> {
252    position: Option<O>,
253    offsets: Vec<LONG>,
254    data: Vec<T>,
255    _phantom: PhantomData<O>,
256}
257
258impl<T: Datablock> AllocatedFieldValues<u32> for AllocatedOffsets<T, u32> {
259    fn count(&self) -> u32 {
260        self.data.len() as u32
261    }
262
263    fn size(&self) -> u32 {
264        <LONG as TiffType<u32>>::size() * self.count()
265    }
266
267    fn position(&self) -> Option<u32> {
268        self.position
269    }
270
271    fn type_id(&self) -> u16 {
272        <LONG as TiffType<u32>>::id()
273    }
274
275    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
276        let unboxed = *self;
277        let AllocatedOffsets { data, offsets, .. } = unboxed;
278        for offset in offsets {
279            <LONG as TiffType<u32>>::write_to(offset, file)?;
280        }
281        for block in data {
282            let file_initial = file.written_bytes();
283            let block_size = block.size() as u64;
284            block.write_to(file)?;
285            let written_size = file.written_bytes() - file_initial;
286            if written_size % 2 == 1 {
287                file.write_arbitrary_byte()?
288            }
289            if written_size != block_size {
290                panic!(
291                    "The number of bytes allocated by the Datablock ({}) is different from the number of bytes written to the file ({}).",
292                    block_size, written_size
293                )
294            }
295        }
296        Ok(())
297    }
298}
299
300/// Allocated form of `Offsets` for u64
301struct AllocatedOffsets64<T: Datablock> {
302    position: Option<u64>,
303    offsets: Vec<LONG8>,
304    data: Vec<T>,
305}
306
307impl<T: Datablock> AllocatedFieldValues<u64> for AllocatedOffsets64<T> {
308    fn count(&self) -> u64 {
309        self.data.len() as u64
310    }
311
312    fn size(&self) -> u64 {
313        <LONG8 as TiffType<u64>>::size() * self.count()
314    }
315
316    fn position(&self) -> Option<u64> {
317        self.position
318    }
319
320    fn type_id(&self) -> u16 {
321        <LONG8 as TiffType<u64>>::id()
322    }
323
324    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
325        let unboxed = *self;
326        let AllocatedOffsets64 { data, offsets, .. } = unboxed;
327        for offset in offsets {
328            <LONG8 as TiffType<u64>>::write_to(offset, file)?;
329        }
330        for block in data {
331            let file_initial = file.written_bytes();
332            let block_size = block.size() as u64;
333            block.write_to(file)?;
334            let written_size = file.written_bytes() - file_initial;
335            if written_size % 2 == 1 {
336                file.write_arbitrary_byte()?
337            }
338            if written_size != block_size {
339                panic!(
340                    "The number of bytes allocated by the Datablock ({}) is different from the number of bytes written to the file ({}).",
341                    block_size, written_size
342                )
343            }
344        }
345        Ok(())
346    }
347}
348
349/// A list of values of any given [`TiffType`].
350///
351/// Generic over `O: OffsetSize` to support both TIFF (u32) and BigTIFF (u64).
352///
353/// [`TiffType`]: ../types/trait.TiffType.html
354#[derive(Debug, PartialEq)]
355pub struct TiffTypeValues<T, O: OffsetSize = u32> {
356    values: Vec<T>,
357    _phantom: PhantomData<O>,
358}
359
360impl<T, O: OffsetSize> TiffTypeValues<T, O> {
361    /// Creates a new instance of `TiffTypeValues` from a vector
362    /// of instances of any given [`TiffType`].
363    ///
364    /// [`TiffType`]: ../types/trait.TiffType.html
365    pub fn new(values: Vec<T>) -> Self {
366        if values.is_empty() {
367            panic!("Cannot create an empty instance of TiffTypeValues")
368        }
369        TiffTypeValues {
370            values,
371            _phantom: PhantomData,
372        }
373    }
374}
375
376impl<T: TiffType<O> + 'static, O: OffsetSize> FieldValues<O> for TiffTypeValues<T, O> {
377    fn count(&self) -> O {
378        O::from_usize(self.values.len())
379    }
380
381    fn size(&self) -> O {
382        T::size() * self.count()
383    }
384
385    fn allocate(self: Box<Self>, c: &mut Cursor<O>) -> Box<dyn AllocatedFieldValues<O>> {
386        let position = if self.size().to_u64() <= O::INLINE_THRESHOLD.to_u64() {
387            None
388        } else {
389            let size = self.size() + self.size() % O::TWO;
390            let pos = c.allocated_bytes();
391            c.allocate(size);
392            Some(pos)
393        };
394
395        Box::new(AllocatedTiffTypeValues {
396            position,
397            values: self.values,
398            _phantom: PhantomData,
399        })
400    }
401}
402
403/// Allocated form of `TiffTypeValues`
404struct AllocatedTiffTypeValues<T, O: OffsetSize> {
405    position: Option<O>,
406    values: Vec<T>,
407    _phantom: PhantomData<O>,
408}
409
410impl<T: TiffType<O>, O: OffsetSize> AllocatedFieldValues<O> for AllocatedTiffTypeValues<T, O> {
411    fn count(&self) -> O {
412        O::from_usize(self.values.len())
413    }
414
415    fn size(&self) -> O {
416        T::size() * self.count()
417    }
418
419    fn position(&self) -> Option<O> {
420        self.position
421    }
422
423    fn type_id(&self) -> u16 {
424        T::id()
425    }
426
427    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
428        let size = self.size();
429        for value in self.values {
430            let file_initial = file.written_bytes();
431            value.write_to(file)?;
432            let written_size = file.written_bytes() - file_initial;
433            if written_size != T::size().to_u64() {
434                panic!(
435                    "The size indicated ({}) is different from the number of bytes the type has written to the file ({}).",
436                    T::size().to_u64(),
437                    written_size
438                )
439            }
440        }
441
442        if size.to_u64() % 2 == 1 && size.to_u64() > O::INLINE_THRESHOLD.to_u64() {
443            file.write_arbitrary_byte()?;
444        }
445        Ok(())
446    }
447}
448
449/// A list of [`IFD`] values, each pointing to a specific [`Ifd`].
450///
451/// Generic over `O: OffsetSize` to support both TIFF (u32) and BigTIFF (u64).
452///
453/// This structure owns a list of [`IfdChain`]s instead, so the user
454/// doesn't have to deal with the offsets in the file. Each [`IFD`]
455/// value will point to the first element of each [`IfdChain`]. Each
456/// of those `Ifd`s will point to the next one in their chain (if they
457/// are not the last of their chain) and so on.
458///
459/// It is responsible for writing both the offsets and all the [`Ifd`]s.
460///
461/// [`LONG`]: ../types/struct.LONG.html
462/// [`IFD`]: ../types/struct.IFD.html
463/// [`Ifd`]: ../struct.Ifd.html
464/// [`IfdChain`]: ../struct.IfdChain.html
465pub struct OffsetsToIfds<O: OffsetSize = u32> {
466    pub data: Vec<IfdChain<O>>,
467}
468
469impl<O: OffsetSize> OffsetsToIfds<O> {
470    /// Creates a new `OffsetsToIfds` instance from a vector of [`IfdChain`]s.
471    ///
472    /// [`IfdChain`]: ../struct.IfdChain.html
473    pub fn new(ifds: Vec<IfdChain<O>>) -> Self {
474        OffsetsToIfds { data: ifds }
475    }
476}
477
478impl FieldValues<u32> for OffsetsToIfds<u32> {
479    fn count(&self) -> u32 {
480        self.data.len() as u32
481    }
482
483    fn size(&self) -> u32 {
484        <IFD as TiffType<u32>>::size() * self.count()
485    }
486
487    fn allocate(self: Box<Self>, c: &mut Cursor<u32>) -> Box<dyn AllocatedFieldValues<u32>> {
488        let position = Some(c.allocated_bytes());
489        if self.data.len() == 1 {
490            let offsets = Vec::new();
491            let ifd = self.data.into_iter().next().unwrap();
492            let allocated_data = vec![ifd.allocate(c)];
493
494            Box::new(AllocatedOffsetsToIfds {
495                position,
496                offsets,
497                data: allocated_data,
498            })
499        } else {
500            c.allocate(self.size());
501            let mut offsets = Vec::with_capacity(self.data.len());
502            let mut allocated_data = Vec::with_capacity(self.data.len());
503
504            for ifd in self.data {
505                offsets.push(IFD(c.allocated_bytes()));
506                allocated_data.push(ifd.allocate(c));
507            }
508
509            Box::new(AllocatedOffsetsToIfds {
510                position,
511                offsets,
512                data: allocated_data,
513            })
514        }
515    }
516}
517
518impl FieldValues<u64> for OffsetsToIfds<u64> {
519    fn count(&self) -> u64 {
520        self.data.len() as u64
521    }
522
523    fn size(&self) -> u64 {
524        <BIGIFD as TiffType<u64>>::size() * self.count()
525    }
526
527    fn allocate(self: Box<Self>, c: &mut Cursor<u64>) -> Box<dyn AllocatedFieldValues<u64>> {
528        let position = Some(c.allocated_bytes());
529        if self.data.len() == 1 {
530            let offsets = Vec::new();
531            let ifd = self.data.into_iter().next().unwrap();
532            let allocated_data = vec![ifd.allocate(c)];
533
534            Box::new(AllocatedOffsetsToIfds64 {
535                position,
536                offsets,
537                data: allocated_data,
538            })
539        } else {
540            c.allocate(self.size());
541            let mut offsets = Vec::with_capacity(self.data.len());
542            let mut allocated_data = Vec::with_capacity(self.data.len());
543
544            for ifd in self.data {
545                offsets.push(BIGIFD(c.allocated_bytes()));
546                allocated_data.push(ifd.allocate(c));
547            }
548
549            Box::new(AllocatedOffsetsToIfds64 {
550                position,
551                offsets,
552                data: allocated_data,
553            })
554        }
555    }
556}
557
558/// Allocated form of `OffsetsToIfds` for u32
559struct AllocatedOffsetsToIfds {
560    position: Option<u32>,
561    offsets: Vec<IFD>,
562    data: Vec<AllocatedIfdChain<u32>>,
563}
564
565impl AllocatedFieldValues<u32> for AllocatedOffsetsToIfds {
566    fn count(&self) -> u32 {
567        self.data.len() as u32
568    }
569
570    fn size(&self) -> u32 {
571        <IFD as TiffType<u32>>::size() * self.count()
572    }
573
574    fn position(&self) -> Option<u32> {
575        self.position
576    }
577
578    fn type_id(&self) -> u16 {
579        <IFD as TiffType<u32>>::id()
580    }
581
582    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
583        let unboxed = *self;
584        let AllocatedOffsetsToIfds { data, offsets, .. } = unboxed;
585        for offset in offsets {
586            <IFD as TiffType<u32>>::write_to(offset, file)?;
587        }
588        for ifd in data.into_iter() {
589            ifd.write_to(file)?;
590        }
591        Ok(())
592    }
593}
594
595/// Allocated form of `OffsetsToIfds` for u64
596struct AllocatedOffsetsToIfds64 {
597    position: Option<u64>,
598    offsets: Vec<BIGIFD>,
599    data: Vec<AllocatedIfdChain<u64>>,
600}
601
602impl AllocatedFieldValues<u64> for AllocatedOffsetsToIfds64 {
603    fn count(&self) -> u64 {
604        self.data.len() as u64
605    }
606
607    fn size(&self) -> u64 {
608        <BIGIFD as TiffType<u64>>::size() * self.count()
609    }
610
611    fn position(&self) -> Option<u64> {
612        self.position
613    }
614
615    fn type_id(&self) -> u16 {
616        <BIGIFD as TiffType<u64>>::id()
617    }
618
619    fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
620        let unboxed = *self;
621        let AllocatedOffsetsToIfds64 { data, offsets, .. } = unboxed;
622        for offset in offsets {
623            <BIGIFD as TiffType<u64>>::write_to(offset, file)?;
624        }
625        for ifd in data.into_iter() {
626            ifd.write_to(file)?;
627        }
628        Ok(())
629    }
630}