From b79128d766e6c9721bca3abcee84952aecac9cac Mon Sep 17 00:00:00 2001 From: Masterchef365 Date: Mon, 12 Aug 2019 15:59:44 -0700 Subject: [PATCH 1/5] Add Deflate and OldDeflate compression type recognition --- src/decoder/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index dd1ad32..dcda365 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -100,6 +100,8 @@ pub enum CompressionMethod { Fax4 = 4, LZW = 5, JPEG = 6, + Deflate = 8, + OldDeflate = 0x80B2, PackBits = 0x8005, } From ce73d39d57c8a944d4901fb0071b0c2ab48f5c9c Mon Sep 17 00:00:00 2001 From: Masterchef365 Date: Mon, 12 Aug 2019 16:10:01 -0700 Subject: [PATCH 2/5] Add old-style DEFLATE support --- Cargo.toml | 1 + src/decoder/mod.rs | 8 +++++++- src/decoder/stream.rs | 35 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cd87c01..3db3078 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ byteorder = "1.2" lzw = "0.10" num-derive = "0.3" num-traits = "0.2" +libflate = "0.1" # TODO: Put behind a feature? [dev-dependencies] tempfile = "3.0" diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index dcda365..0aab11f 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -7,7 +7,7 @@ use {ColorType, TiffError, TiffFormatError, TiffResult, TiffUnsupportedError}; use self::ifd::Directory; -use self::stream::{ByteOrder, EndianReader, LZWReader, PackBitsReader, SmartReader}; +use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader}; pub mod ifd; mod stream; @@ -579,6 +579,12 @@ impl Decoder { )?; (bytes, Box::new(reader)) } + CompressionMethod::OldDeflate => { + let (bytes, reader) = DeflateReader::new( + &mut self.reader, + max_uncompressed_length)?; + (bytes, Box::new(reader)) + } method => { return Err(TiffError::UnsupportedError( TiffUnsupportedError::UnsupportedCompressionMethod(method), diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 46d7d10..f1d3c31 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -3,6 +3,7 @@ use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use lzw; use std::io::{self, Read, Seek}; +use libflate::zlib; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -67,6 +68,40 @@ pub trait EndianReader: Read { } } +/// Reader that decompresses DEFLATE streams +pub struct DeflateReader { + buffer: io::Cursor>, + byte_order: ByteOrder, +} + +impl DeflateReader { + pub fn new( + reader: &mut SmartReader, + max_uncompressed_length: usize, + ) -> io::Result<(usize, Self)> { + let byte_order = reader.byte_order; + let mut decoder = zlib::Decoder::new(reader)?; + let mut uncompressed = Vec::with_capacity(max_uncompressed_length); + let bytes_read = decoder.read_to_end(&mut uncompressed)?; + Ok((bytes_read, Self { + byte_order, + buffer: io::Cursor::new(uncompressed), + })) + } +} + +impl Read for DeflateReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.buffer.read(buf) + } +} + +impl EndianReader for DeflateReader { + fn byte_order(&self) -> ByteOrder { + self.byte_order + } +} + /// Reader that decompresses LZW streams pub struct LZWReader { buffer: io::Cursor>, diff --git a/src/lib.rs b/src/lib.rs index 8ed1d25..64a31cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate byteorder; extern crate lzw; +extern crate libflate; #[macro_use] extern crate num_derive; extern crate num_traits; From 205626bd621e87f0c3531b52050611c00f70e838 Mon Sep 17 00:00:00 2001 From: Masterchef365 Date: Fri, 16 Aug 2019 10:30:16 -0700 Subject: [PATCH 3/5] Switch to miniz_oxide crate for DEFLATE decompression --- Cargo.toml | 2 +- src/decoder/mod.rs | 4 +--- src/decoder/stream.rs | 22 +++++++++++++--------- src/error.rs | 2 +- src/lib.rs | 2 +- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3db3078..3afb300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ byteorder = "1.2" lzw = "0.10" num-derive = "0.3" num-traits = "0.2" -libflate = "0.1" # TODO: Put behind a feature? +miniz_oxide = "0.3" # TODO: Put behind a feature? [dev-dependencies] tempfile = "3.0" diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 0aab11f..c67df27 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -580,9 +580,7 @@ impl Decoder { (bytes, Box::new(reader)) } CompressionMethod::OldDeflate => { - let (bytes, reader) = DeflateReader::new( - &mut self.reader, - max_uncompressed_length)?; + let (bytes, reader) = DeflateReader::new(&mut self.reader)?; (bytes, Box::new(reader)) } method => { diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index f1d3c31..c5c0a44 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -3,7 +3,7 @@ use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use lzw; use std::io::{self, Read, Seek}; -use libflate::zlib; +use miniz_oxide::inflate; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -77,16 +77,20 @@ pub struct DeflateReader { impl DeflateReader { pub fn new( reader: &mut SmartReader, - max_uncompressed_length: usize, ) -> io::Result<(usize, Self)> { let byte_order = reader.byte_order; - let mut decoder = zlib::Decoder::new(reader)?; - let mut uncompressed = Vec::with_capacity(max_uncompressed_length); - let bytes_read = decoder.read_to_end(&mut uncompressed)?; - Ok((bytes_read, Self { - byte_order, - buffer: io::Cursor::new(uncompressed), - })) + let mut compressed = Vec::new(); + reader.read_to_end(&mut compressed)?; + let uncompressed = inflate::decompress_to_vec_zlib(&compressed).map_err(|error| { + std::io::Error::new(std::io::ErrorKind::InvalidData, format!("DEFLATE error: {:?}", error)) + })?; + Ok(( + uncompressed.len(), + Self { + byte_order, + buffer: io::Cursor::new(uncompressed), + }, + )) } } diff --git a/src/error.rs b/src/error.rs index 4e9f127..69402b0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -138,7 +138,7 @@ impl Error for TiffError { } } - fn cause(&self) -> Option<&Error> { + fn cause(&self) -> Option<& dyn Error> { match *self { TiffError::IoError(ref e) => Some(e), _ => None, diff --git a/src/lib.rs b/src/lib.rs index 64a31cd..fe194e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ extern crate byteorder; extern crate lzw; -extern crate libflate; +extern crate miniz_oxide; #[macro_use] extern crate num_derive; extern crate num_traits; From 6f9f2adfbfbabd1fbe93667155baea303a982713 Mon Sep 17 00:00:00 2001 From: Masterchef365 Date: Wed, 2 Oct 2019 21:06:11 -0700 Subject: [PATCH 4/5] Improved error messages for inflate decompression --- Cargo.toml | 2 +- src/decoder/stream.rs | 12 +++++------- src/error.rs | 22 +++++++++++++++++++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3afb300..bdba5b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ byteorder = "1.2" lzw = "0.10" num-derive = "0.3" num-traits = "0.2" -miniz_oxide = "0.3" # TODO: Put behind a feature? +miniz_oxide = "0.3" [dev-dependencies] tempfile = "3.0" diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index c5c0a44..58a6350 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,9 +1,10 @@ //! All IO functionality needed for TIFF decoding +use crate::error::TiffResult; use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use lzw; -use std::io::{self, Read, Seek}; use miniz_oxide::inflate; +use std::io::{self, Read, Seek}; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -75,15 +76,12 @@ pub struct DeflateReader { } impl DeflateReader { - pub fn new( - reader: &mut SmartReader, - ) -> io::Result<(usize, Self)> { + pub fn new(reader: &mut SmartReader) -> TiffResult<(usize, Self)> { let byte_order = reader.byte_order; let mut compressed = Vec::new(); reader.read_to_end(&mut compressed)?; - let uncompressed = inflate::decompress_to_vec_zlib(&compressed).map_err(|error| { - std::io::Error::new(std::io::ErrorKind::InvalidData, format!("DEFLATE error: {:?}", error)) - })?; + let uncompressed = inflate::decompress_to_vec_zlib(&compressed)?; + Ok(( uncompressed.len(), Self { diff --git a/src/error.rs b/src/error.rs index 69402b0..cbe7e3f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,6 +5,7 @@ use std::string; use decoder::ifd::{Tag, Value}; use decoder::{CompressionMethod, PhotometricInterpretation, PlanarConfiguration}; +use miniz_oxide::inflate::TINFLStatus; use ColorType; /// Tiff error kinds. @@ -34,6 +35,7 @@ pub enum TiffFormatError { UnknownPredictor(u32), UnsignedIntegerExpected(Value), SignedIntegerExpected(Value), + InflateError(InflateError), } impl fmt::Display for TiffFormatError { @@ -55,10 +57,28 @@ impl fmt::Display for TiffFormatError { SignedIntegerExpected(ref val) => { write!(fmt, "Expected signed integer, {:?} found.", val) } + InflateError(_) => write!(fmt, "Failed to decode inflate data."), } } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct InflateError { + status: TINFLStatus, +} + +impl From for TiffFormatError { + fn from(status: TINFLStatus) -> Self { + TiffFormatError::InflateError(InflateError { status }) + } +} + +impl From for TiffError { + fn from(status: TINFLStatus) -> Self { + TiffError::FormatError(status.into()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TiffUnsupportedError { HorizontalPredictor(ColorType), @@ -138,7 +158,7 @@ impl Error for TiffError { } } - fn cause(&self) -> Option<& dyn Error> { + fn cause(&self) -> Option<&dyn Error> { match *self { TiffError::IoError(ref e) => Some(e), _ => None, From f8d4b57120fcc8553856988cf818d6a2825dbda0 Mon Sep 17 00:00:00 2001 From: Masterchef365 Date: Fri, 4 Oct 2019 18:52:39 -0700 Subject: [PATCH 5/5] Change implementation details for InflateError, add uncompressed cap --- src/decoder/mod.rs | 2 +- src/decoder/stream.rs | 17 ++++++++++++++--- src/error.rs | 12 ++++++------ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index c67df27..8c333ec 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -580,7 +580,7 @@ impl Decoder { (bytes, Box::new(reader)) } CompressionMethod::OldDeflate => { - let (bytes, reader) = DeflateReader::new(&mut self.reader)?; + let (bytes, reader) = DeflateReader::new(&mut self.reader, max_uncompressed_length)?; (bytes, Box::new(reader)) } method => { diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 58a6350..fe5d20c 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,6 +1,6 @@ //! All IO functionality needed for TIFF decoding -use crate::error::TiffResult; +use crate::error::{TiffError, TiffResult}; use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; use lzw; use miniz_oxide::inflate; @@ -76,11 +76,22 @@ pub struct DeflateReader { } impl DeflateReader { - pub fn new(reader: &mut SmartReader) -> TiffResult<(usize, Self)> { + pub fn new( + reader: &mut SmartReader, + max_uncompressed_length: usize, + ) -> TiffResult<(usize, Self)> { let byte_order = reader.byte_order; let mut compressed = Vec::new(); reader.read_to_end(&mut compressed)?; - let uncompressed = inflate::decompress_to_vec_zlib(&compressed)?; + + // TODO: Implement streaming compression, and remove this (temporary) and somewhat + // misleading workaround. + if compressed.len() > max_uncompressed_length { + return Err(TiffError::LimitsExceeded); + } + + let uncompressed = + inflate::decompress_to_vec_zlib(&compressed).map_err(TiffError::from_inflate_status)?; Ok(( uncompressed.len(), diff --git a/src/error.rs b/src/error.rs index cbe7e3f..6c6021b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -67,15 +67,15 @@ pub struct InflateError { status: TINFLStatus, } -impl From for TiffFormatError { - fn from(status: TINFLStatus) -> Self { - TiffFormatError::InflateError(InflateError { status }) +impl InflateError { + pub fn new(status: TINFLStatus) -> Self { + Self { status } } } -impl From for TiffError { - fn from(status: TINFLStatus) -> Self { - TiffError::FormatError(status.into()) +impl TiffError { + pub(crate) fn from_inflate_status(status: TINFLStatus) -> Self { + TiffError::FormatError(TiffFormatError::InflateError(InflateError::new(status))) } }