diff --git a/src/encoder.rs b/src/encoder.rs index 6ac55a2..159421f 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -39,9 +39,16 @@ impl fmt::Display for EncodingFormatError { } } -#[derive(Debug)] /// Encoding error. +#[derive(Debug)] +#[non_exhaustive] pub enum EncodingError { + /// Frame buffer is too small for the declared dimensions. + FrameBufferTooSmallForDimensions, + /// Failed to internally allocate a buffer of sufficient size. + OutOfMemory, + /// Expected a writer but none found. + WriterNotFound, /// Returned if the to image is not encodable as a gif. Format(EncodingFormatError), /// Wraps `std::io::Error`. @@ -52,6 +59,11 @@ impl fmt::Display for EncodingError { #[cold] fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Self::FrameBufferTooSmallForDimensions => { + fmt.write_str("Frame Buffer Too Small for Dimensions") + } + Self::OutOfMemory => fmt.write_str("Out of Memory"), + Self::WriterNotFound => fmt.write_str("Writer Not Found"), Self::Io(err) => err.fmt(fmt), Self::Format(err) => err.fmt(fmt), } @@ -62,6 +74,9 @@ impl error::Error for EncodingError { #[cold] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { + Self::FrameBufferTooSmallForDimensions => None, + Self::OutOfMemory => None, + Self::WriterNotFound => None, Self::Io(err) => Some(err), Self::Format(err) => Some(err), } @@ -188,11 +203,7 @@ impl Encoder { .checked_mul(usize::from(frame.height)) .map_or(true, |size| frame.buffer.len() < size) { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "frame.buffer is too small for its width/height", - ) - .into()); + return Err(EncodingError::FrameBufferTooSmallForDimensions); } debug_assert!( (frame.width > 0 && frame.height > 0) || frame.buffer.is_empty(), @@ -246,13 +257,10 @@ impl Encoder { self.buffer.clear(); self.buffer .try_reserve(data.len() / 4) - .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; + .map_err(|_| EncodingError::OutOfMemory)?; lzw_encode(data, &mut self.buffer); - let writer = self - .w - .as_mut() - .ok_or_else(|| io::Error::from(io::ErrorKind::Unsupported))?; + let writer = self.w.as_mut().ok_or(EncodingError::WriterNotFound)?; Self::write_encoded_image_block(writer, &self.buffer) } @@ -346,7 +354,11 @@ impl Encoder { /// This method can be used to write an unsupported extension to the file. `func` is the extension /// identifier (e.g. `Extension::Application as u8`). `data` are the extension payload blocks. If any /// contained slice has a lenght > 255 it is automatically divided into sub-blocks. - pub fn write_raw_extension(&mut self, func: AnyExtension, data: &[&[u8]]) -> io::Result<()> { + pub fn write_raw_extension( + &mut self, + func: AnyExtension, + data: &[&[u8]], + ) -> Result<(), EncodingError> { let writer = self.writer()?; writer.write_le(Block::Extension as u8)?; writer.write_le(func.0)?; @@ -356,7 +368,7 @@ impl Encoder { writer.write_all(chunk)?; } } - writer.write_le(0u8) + Ok(writer.write_le(0u8)?) } /// Writes a frame to the image, but expects `Frame.buffer` to contain LZW-encoded data @@ -379,7 +391,7 @@ impl Encoder { } /// Writes the logical screen desriptor - fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> { + fn write_screen_desc(&mut self, flags: u8) -> Result<(), EncodingError> { let mut tmp = tmp_buf::<13>(); tmp.write_all(b"GIF89a")?; tmp.write_le(self.width)?; @@ -387,7 +399,7 @@ impl Encoder { tmp.write_le(flags)?; // packed field tmp.write_le(0u8)?; // bg index tmp.write_le(0u8)?; // aspect ratio - tmp.finish(self.writer()?) + Ok(tmp.finish(self.writer()?)?) } /// Gets a reference to the writer instance used by this encoder. @@ -403,23 +415,19 @@ impl Encoder { } /// Finishes writing, and returns the `io::Write` instance used by this encoder - pub fn into_inner(mut self) -> io::Result { + pub fn into_inner(mut self) -> Result { self.write_trailer()?; - self.w - .take() - .ok_or_else(|| io::Error::from(io::ErrorKind::Unsupported)) + self.w.take().ok_or(EncodingError::WriterNotFound) } /// Write the final tailer. - fn write_trailer(&mut self) -> io::Result<()> { - self.writer()?.write_le(Block::Trailer as u8) + fn write_trailer(&mut self) -> Result<(), EncodingError> { + Ok(self.writer()?.write_le(Block::Trailer as u8)?) } #[inline] - fn writer(&mut self) -> io::Result<&mut W> { - self.w - .as_mut() - .ok_or_else(|| io::Error::from(io::ErrorKind::Unsupported)) + fn writer(&mut self) -> Result<&mut W, EncodingError> { + self.w.as_mut().ok_or(EncodingError::WriterNotFound) } } diff --git a/src/reader/converter.rs b/src/reader/converter.rs index 24b8f85..2812e16 100644 --- a/src/reader/converter.rs +++ b/src/reader/converter.rs @@ -2,7 +2,6 @@ use alloc::borrow::Cow; use alloc::vec::Vec; use core::iter; use core::mem; -use std::io; use super::decoder::{DecodingError, OutputBuffer, PLTE_CHANNELS}; use crate::common::Frame; @@ -49,7 +48,7 @@ impl PixelConverter { let pixel_bytes = self .memory_limit .buffer_size(self.color_output, frame.width, frame.height) - .ok_or_else(|| io::Error::new(io::ErrorKind::OutOfMemory, "image is too large"))?; + .ok_or_else(|| DecodingError::OutOfMemory)?; debug_assert_eq!( pixel_bytes, diff --git a/src/reader/decoder.rs b/src/reader/decoder.rs index 6b8509f..03068cb 100644 --- a/src/reader/decoder.rs +++ b/src/reader/decoder.rs @@ -39,9 +39,20 @@ impl error::Error for DecodingFormatError { } } -#[derive(Debug)] /// Decoding error. +#[derive(Debug)] +#[non_exhaustive] pub enum DecodingError { + /// Failed to internally allocate a buffer of sufficient size. + OutOfMemory, + /// Expected a decoder but none found. + DecoderNotFound, + /// Expected an end-code, but none found. + EndCodeNotFound, + /// Decoding could not complete as the reader completed prematurely. + UnexpectedEof, + /// Error encountered while decoding an LZW stream. + LzwError(LzwError), /// Returned if the image is found to be malformed. Format(DecodingFormatError), /// Wraps `std::io::Error`. @@ -61,6 +72,11 @@ impl fmt::Display for DecodingError { #[cold] fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { + Self::OutOfMemory => fmt.write_str("Out of Memory"), + Self::DecoderNotFound => fmt.write_str("Decoder Not Found"), + Self::EndCodeNotFound => fmt.write_str("End-Code Not Found"), + Self::UnexpectedEof => fmt.write_str("Unexpected End of File"), + Self::LzwError(ref err) => err.fmt(fmt), Self::Format(ref d) => d.fmt(fmt), Self::Io(ref err) => err.fmt(fmt), } @@ -71,23 +87,28 @@ impl error::Error for DecodingError { #[cold] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { + Self::OutOfMemory => None, + Self::DecoderNotFound => None, + Self::EndCodeNotFound => None, + Self::UnexpectedEof => None, + Self::LzwError(ref err) => Some(err), Self::Format(ref err) => Some(err), Self::Io(ref err) => Some(err), } } } -impl From for DecodingError { +impl From for DecodingError { #[inline] - fn from(err: io::Error) -> Self { - Self::Io(err) + fn from(err: LzwError) -> Self { + Self::LzwError(err) } } -impl From for DecodingError { - #[cold] - fn from(err: io::ErrorKind) -> Self { - Self::Io(io::Error::from(err)) +impl From for DecodingError { + #[inline] + fn from(err: io::Error) -> Self { + Self::Io(err) } } @@ -297,32 +318,37 @@ impl LzwReader { &mut self, lzw_data: &[u8], decode_buffer: &mut OutputBuffer<'_>, - ) -> io::Result<(usize, usize)> { - let decoder = self.decoder.as_mut().ok_or(io::ErrorKind::Unsupported)?; + ) -> Result<(usize, usize), DecodingError> { + let decoder = self + .decoder + .as_mut() + .ok_or(DecodingError::DecoderNotFound)?; - let decode_buffer = match decode_buffer { - OutputBuffer::Slice(buf) => &mut **buf, - OutputBuffer::None => &mut [], - OutputBuffer::Vec(_) => return Err(io::Error::from(io::ErrorKind::Unsupported)), + let (status, consumed_in, consumed_out) = match decode_buffer { + OutputBuffer::Slice(buf) => { + let decoded = decoder.decode_bytes(lzw_data, &mut **buf); + (decoded.status, decoded.consumed_in, decoded.consumed_out) + } + OutputBuffer::None => { + let decoded = decoder.decode_bytes(lzw_data, &mut []); + (decoded.status, decoded.consumed_in, decoded.consumed_out) + } + OutputBuffer::Vec(buf) => { + let decoded = decoder.into_vec(buf).decode(lzw_data); + (decoded.status, decoded.consumed_in, decoded.consumed_out) + } }; - let decoded = decoder.decode_bytes(lzw_data, decode_buffer); - - match decoded.status { - Ok(LzwStatus::Done | LzwStatus::Ok) => {} - Ok(LzwStatus::NoProgress) => { + match status? { + LzwStatus::Done | LzwStatus::Ok => {} + LzwStatus::NoProgress => { if self.check_for_end_code { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "no end code in lzw stream", - )); + return Err(DecodingError::EndCodeNotFound); } } - Err(err @ LzwError::InvalidCode) => { - return Err(io::Error::new(io::ErrorKind::InvalidData, err)); - } } - Ok((decoded.consumed_in, decoded.consumed_out)) + + Ok((consumed_in, consumed_out)) } } @@ -393,7 +419,7 @@ impl OutputBuffer<'_> { let len = buf.len(); memory_limit.check_size(vec.len() + len)?; vec.try_reserve(len) - .map_err(|_| io::ErrorKind::OutOfMemory)?; + .map_err(|_| DecodingError::OutOfMemory)?; if vec.capacity() - vec.len() >= len { vec.extend_from_slice(buf); } @@ -561,7 +587,7 @@ impl StreamingDecoder { }) ); - let b = *buf.first().ok_or(io::ErrorKind::UnexpectedEof)?; + let b = *buf.first().ok_or(DecodingError::UnexpectedEof)?; match self.state { Magic => { @@ -588,7 +614,7 @@ impl StreamingDecoder { let table_size = PLTE_CHANNELS * (1 << ((global_flags & 0b111) + 1) as usize); self.global_color_table .try_reserve_exact(table_size) - .map_err(|_| io::ErrorKind::OutOfMemory)?; + .map_err(|_| DecodingError::OutOfMemory)?; table_size } else { 0usize @@ -632,7 +658,7 @@ impl StreamingDecoder { .palette .get_or_insert_with(Vec::new) .try_reserve_exact(pal_len) - .map_err(|_| io::ErrorKind::OutOfMemory)?; + .map_err(|_| DecodingError::OutOfMemory)?; goto!(consumed, LocalPalette(pal_len)) } else { goto!(consumed, LocalPalette(0)) @@ -704,7 +730,7 @@ impl StreamingDecoder { self.ext .data .try_reserve(n) - .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; + .map_err(|_| DecodingError::OutOfMemory)?; self.ext.data.extend_from_slice(&buf[..n]); goto!(n, ExtensionDataBlock(left - n)) } else if b == 0 { diff --git a/src/reader/mod.rs b/src/reader/mod.rs index 17310e5..cb71697 100644 --- a/src/reader/mod.rs +++ b/src/reader/mod.rs @@ -210,7 +210,7 @@ impl ReadDecoder { let (consumed, result) = { let buf = self.reader.fill_buf()?; if buf.is_empty() { - return Err(io::ErrorKind::UnexpectedEof.into()); + return Err(DecodingError::UnexpectedEof); } self.decoder.update(buf, write_into)? @@ -368,7 +368,7 @@ where * usize::from(self.current_frame.height) / 4, ) - .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?; + .map_err(|_| DecodingError::OutOfMemory)?; self.copy_lzw_into_buffer(min_code_size, &mut vec)?; self.current_frame.buffer = Cow::Owned(vec); }