Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions src/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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),
}
Expand All @@ -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),
}
Expand Down Expand Up @@ -188,11 +203,7 @@ impl<W: Write> Encoder<W> {
.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(),
Expand Down Expand Up @@ -246,13 +257,10 @@ impl<W: Write> Encoder<W> {
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)
}

Expand Down Expand Up @@ -346,7 +354,11 @@ impl<W: Write> Encoder<W> {
/// 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)?;
Expand All @@ -356,7 +368,7 @@ impl<W: Write> Encoder<W> {
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
Expand All @@ -379,15 +391,15 @@ impl<W: Write> Encoder<W> {
}

/// 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)?;
tmp.write_le(self.height)?;
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.
Expand All @@ -403,23 +415,19 @@ impl<W: Write> Encoder<W> {
}

/// Finishes writing, and returns the `io::Write` instance used by this encoder
pub fn into_inner(mut self) -> io::Result<W> {
pub fn into_inner(mut self) -> Result<W, EncodingError> {
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)
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/reader/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
90 changes: 58 additions & 32 deletions src/reader/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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),
}
Expand All @@ -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<io::Error> for DecodingError {
impl From<LzwError> for DecodingError {
#[inline]
fn from(err: io::Error) -> Self {
Self::Io(err)
fn from(err: LzwError) -> Self {
Self::LzwError(err)
}
}

impl From<io::ErrorKind> for DecodingError {
#[cold]
fn from(err: io::ErrorKind) -> Self {
Self::Io(io::Error::from(err))
impl From<io::Error> for DecodingError {
#[inline]
fn from(err: io::Error) -> Self {
Self::Io(err)
}
}

Expand Down Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 => {
Expand All @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions src/reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl<R: Read> ReadDecoder<R> {
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)?
Expand Down Expand Up @@ -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);
}
Expand Down
Loading