use std::fs;
use std::io;
use std::io::Write;
use byteorder::{BigEndian, LittleEndian, WriteBytesExt};
use crate::ifd::values::Offsets;
#[derive(Clone, Copy)]
pub enum Endianness {
II,
MM,
}
impl Endianness {
pub(crate) fn id(&self) -> u16 {
match &self {
Endianness::II => 0x4949,
Endianness::MM => 0x4d4d,
}
}
}
#[doc(hidden)]
pub struct Cursor(u32);
impl Cursor {
pub(crate) fn new() -> Self {
Cursor(0)
}
pub(crate) fn allocate(&mut self, n: u32) {
self.0 = match self.0.checked_add(n) {
Some(val) => val,
None => panic!("Attempted to write a TIFF file bigger than 2**32 bytes."),
};
}
pub(crate) fn allocated_bytes(&self) -> u32 {
self.0
}
}
pub struct EndianFile {
file: fs::File,
byte_order: Endianness,
written_bytes: u32,
}
impl Into<fs::File> for EndianFile {
fn into(self) -> fs::File {
self.file
}
}
impl EndianFile {
pub(crate) fn new(file: fs::File, byte_order: Endianness) -> Self {
Self {
file,
byte_order,
written_bytes: 0,
}
}
pub(crate) fn written_bytes(&self) -> u32 {
self.written_bytes
}
}
impl EndianFile {
pub fn write_u8(&mut self, n: u8) -> io::Result<()> {
self.written_bytes += 1;
self.file.write_u8(n)
}
pub fn write_all_u8(&mut self, bytes: &[u8]) -> io::Result<()> {
self.written_bytes += bytes.len() as u32;
self.file.write_all(bytes)
}
pub fn write_u16(&mut self, n: u16) -> io::Result<()> {
self.written_bytes += 2;
match self.byte_order {
Endianness::II => {
self.file.write_u16::<LittleEndian>(n)?;
}
Endianness::MM => {
self.file.write_u16::<BigEndian>(n)?;
}
}
Ok(())
}
pub fn write_u32(&mut self, n: u32) -> io::Result<()> {
self.written_bytes += 4;
match self.byte_order {
Endianness::II => {
self.file.write_u32::<LittleEndian>(n)?;
}
Endianness::MM => {
self.file.write_u32::<BigEndian>(n)?;
}
}
Ok(())
}
pub fn write_i8(&mut self, n: i8) -> io::Result<()> {
self.written_bytes += 1;
self.file.write_i8(n)
}
pub fn write_i16(&mut self, n: i16) -> io::Result<()> {
self.written_bytes += 2;
match self.byte_order {
Endianness::II => {
self.file.write_i16::<LittleEndian>(n)?;
}
Endianness::MM => {
self.file.write_i16::<BigEndian>(n)?;
}
}
Ok(())
}
pub fn write_i32(&mut self, n: i32) -> io::Result<()> {
self.written_bytes += 4;
match self.byte_order {
Endianness::II => {
self.file.write_i32::<LittleEndian>(n)?;
}
Endianness::MM => {
self.file.write_i32::<BigEndian>(n)?;
}
}
Ok(())
}
pub fn write_f32(&mut self, n: f32) -> io::Result<()> {
self.written_bytes += 4;
match self.byte_order {
Endianness::II => {
self.file.write_f32::<LittleEndian>(n)?;
}
Endianness::MM => {
self.file.write_f32::<BigEndian>(n)?;
}
}
Ok(())
}
pub fn write_f64(&mut self, n: f64) -> io::Result<()> {
self.written_bytes += 8;
match self.byte_order {
Endianness::II => {
self.file.write_f64::<LittleEndian>(n)?;
}
Endianness::MM => {
self.file.write_f64::<BigEndian>(n)?;
}
}
Ok(())
}
pub(crate) fn write_arbitrary_byte(&mut self) -> io::Result<()> {
self.written_bytes += 1;
self.file.write_u8(0)
}
}
pub trait Datablock {
fn size(&self) -> u32;
fn write_to(self, file: &mut EndianFile) -> io::Result<()>;
}
pub struct ByteBlock(pub Vec<u8>);
impl ByteBlock {
pub fn offsets(blocks: Vec<Vec<u8>>) -> Offsets<ByteBlock> {
Offsets::new(blocks.into_iter().map(|block| ByteBlock(block)).collect())
}
pub fn single(block: Vec<u8>) -> Offsets<ByteBlock> {
ByteBlock::offsets(vec![block])
}
}
impl Datablock for ByteBlock {
fn size(&self) -> u32 {
self.0.len() as u32
}
fn write_to(self, file: &mut EndianFile) -> io::Result<()> {
file.write_all_u8(&self.0)?;
Ok(())
}
}