use std::io;
use crate::ifd::types::{TiffType, IFD, LONG};
use crate::ifd::{AllocatedIfdChain, IfdChain};
use crate::write::{Cursor, Datablock, EndianFile};
pub trait FieldValues: private::Sealed {
#[doc(hidden)]
fn count(&self) -> u32;
#[doc(hidden)]
fn size(&self) -> u32;
#[doc(hidden)]
fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues>;
}
#[doc(hidden)]
pub trait AllocatedFieldValues {
fn count(&self) -> u32;
fn size(&self) -> u32;
fn position(&self) -> Option<u32>;
fn type_id(&self) -> u16;
fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()>;
}
mod private {
pub trait Sealed {}
impl<T: super::Datablock> Sealed for super::Offsets<T> {}
impl<T: super::TiffType> Sealed for super::TiffTypeValues<T> {}
impl Sealed for super::OffsetsToIfds {}
}
pub struct Offsets<T: Datablock> {
pub data: Vec<T>,
}
impl<T: Datablock + 'static> Offsets<T> {
pub fn new(datablocks: Vec<T>) -> Self {
Offsets { data: datablocks }
}
pub fn single(datablock: T) -> Self {
Offsets::new(vec![datablock])
}
}
impl<T: Datablock + 'static> FieldValues for Offsets<T> {
#[doc(hidden)]
fn count(&self) -> u32 {
self.data.len() as u32
}
#[doc(hidden)]
fn size(&self) -> u32 {
LONG::size() * self.count()
}
#[doc(hidden)]
fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues> {
let position = Some(c.allocated_bytes());
if self.data.len() == 1 {
let offsets = Vec::new();
let block_size = self.data.get(0).unwrap().size();
c.allocate(if block_size % 2 == 0 {
block_size
} else {
block_size + 1
});
Box::new(AllocatedOffsets {
position,
offsets,
data: self.data,
})
} else {
c.allocate(self.size());
let mut offsets = Vec::with_capacity(self.data.len());
for block in self.data.iter() {
offsets.push(LONG(c.allocated_bytes()));
c.allocate(if block.size() % 2 == 0 {
block.size()
} else {
block.size() + 1
});
}
Box::new(AllocatedOffsets {
position,
offsets,
data: self.data,
})
}
}
}
struct AllocatedOffsets<T: Datablock> {
position: Option<u32>,
offsets: Vec<LONG>,
data: Vec<T>,
}
impl<T: Datablock> AllocatedFieldValues for AllocatedOffsets<T> {
fn count(&self) -> u32 {
self.data.len() as u32
}
fn size(&self) -> u32 {
LONG::size() * self.count()
}
fn position(&self) -> Option<u32> {
self.position
}
fn type_id(&self) -> u16 {
LONG::id()
}
fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
let unboxed = *self;
let Self { data, offsets, .. } = unboxed;
for offset in offsets {
offset.write_to(file)?;
}
for block in data {
let file_initial = file.written_bytes();
let block_size = block.size();
block.write_to(file)?;
let written_size = file.written_bytes() - file_initial;
if written_size % 2 == 1 {
file.write_arbitrary_byte()?
}
if written_size != block_size {
panic!(
"The number of bytes allocated by the Datablock ({}) is different from the number of bytes written to the file ({}).",
block_size, written_size
)
}
}
Ok(())
}
}
#[derive(Debug, PartialEq)]
pub struct TiffTypeValues<T: TiffType> {
values: Vec<T>,
}
impl<T: TiffType + 'static> TiffTypeValues<T> {
pub fn new(values: Vec<T>) -> Self {
if values.len() == 0 {
panic!("Cannot create an empty instance of TiffTypeValues")
}
TiffTypeValues { values }
}
}
impl<T: TiffType + 'static> FieldValues for TiffTypeValues<T> {
#[doc(hidden)]
fn count(&self) -> u32 {
self.values.len() as u32
}
#[doc(hidden)]
fn size(&self) -> u32 {
T::size() * self.count()
}
#[doc(hidden)]
fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues> {
let position = if self.size() <= 4 {
None
} else {
let size = self.size() + self.size() % 2;
let pos = c.allocated_bytes();
c.allocate(size);
Some(pos)
};
Box::new(AllocatedTiffTypeValues {
position,
values: self.values,
})
}
}
struct AllocatedTiffTypeValues<T: TiffType> {
position: Option<u32>,
values: Vec<T>,
}
impl<T: TiffType> AllocatedFieldValues for AllocatedTiffTypeValues<T> {
fn count(&self) -> u32 {
self.values.len() as u32
}
fn size(&self) -> u32 {
T::size() * self.count()
}
fn position(&self) -> Option<u32> {
self.position
}
fn type_id(&self) -> u16 {
T::id()
}
fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
let size = self.size();
for value in self.values {
let file_initial = file.written_bytes();
value.write_to(file)?;
let written_size = file.written_bytes() - file_initial;
if written_size != T::size() {
panic!(
"The size indicated ({}) is different from the number of bytes the type has written to the file ({}).",
T::size(), written_size
)
}
}
if size % 2 == 1 && size > 4 {
file.write_arbitrary_byte()?;
}
Ok(())
}
}
pub struct OffsetsToIfds {
pub data: Vec<IfdChain>,
}
impl OffsetsToIfds {
pub fn new(ifds: Vec<IfdChain>) -> Self {
OffsetsToIfds { data: ifds }
}
}
impl FieldValues for OffsetsToIfds {
#[doc(hidden)]
fn count(&self) -> u32 {
self.data.len() as u32
}
#[doc(hidden)]
fn size(&self) -> u32 {
IFD::size() * self.count()
}
#[doc(hidden)]
fn allocate(self: Box<Self>, c: &mut Cursor) -> Box<AllocatedFieldValues> {
let position = Some(c.allocated_bytes());
if self.data.len() == 1 {
let offsets = Vec::new();
let ifd = self.data.into_iter().next().unwrap(); let allocated_data = vec![ifd.allocate(c)];
Box::new(AllocatedOffsetsToIfds {
position,
offsets,
data: allocated_data,
})
} else {
c.allocate(self.size());
let mut offsets = Vec::with_capacity(self.data.len());
let mut allocated_data = Vec::with_capacity(self.data.len());
for ifd in self.data {
offsets.push(IFD(c.allocated_bytes()));
allocated_data.push(ifd.allocate(c));
}
Box::new(AllocatedOffsetsToIfds {
position,
offsets,
data: allocated_data,
})
}
}
}
struct AllocatedOffsetsToIfds {
position: Option<u32>,
offsets: Vec<IFD>,
data: Vec<AllocatedIfdChain>,
}
impl AllocatedFieldValues for AllocatedOffsetsToIfds {
fn count(&self) -> u32 {
self.data.len() as u32
}
fn size(&self) -> u32 {
IFD::size() * self.count()
}
fn position(&self) -> Option<u32> {
self.position
}
fn type_id(&self) -> u16 {
IFD::id()
}
fn write_to(self: Box<Self>, file: &mut EndianFile) -> io::Result<()> {
let unboxed = *self;
let Self { data, offsets, .. } = unboxed;
for offset in offsets {
offset.write_to(file)?;
}
for ifd in data.into_iter() {
ifd.write_to(file)?;
}
Ok(())
}
}