Skip to content

Commit

Permalink
Add BufRead + Seek bound on many decoders (#2149)
Browse files Browse the repository at this point in the history
  • Loading branch information
fintelia authored Feb 19, 2024
1 parent a0ebeab commit 0ed15a8
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 61 deletions.
15 changes: 8 additions & 7 deletions fuzz/fuzzers/fuzzer_script_exr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,13 @@ use image::ExtendedColorType;
use image::ImageDecoder;
use image::ImageEncoder;
use image::ImageResult;
use std::convert::TryFrom;
use std::io::Cursor;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::io::{BufRead, Cursor, Seek, Write};

// "just dont panic"
fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
/// Read the file from the specified path into an `Rgba32FImage`.
// TODO this method should probably already exist in the main image crate
fn read_as_rgba_byte_image(read: impl Read + Seek) -> ImageResult<(u32, u32, Vec<u8>)> {
fn read_as_rgba_byte_image(read: impl BufRead + Seek) -> ImageResult<(u32, u32, Vec<u8>)> {
let mut decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
match usize::try_from(decoder.total_bytes()) {
Ok(decoded_size) if decoded_size <= 256 * 1024 * 1024 => {
Expand All @@ -45,7 +41,12 @@ fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
write: impl Write + Seek,
(width, height, data): &(u32, u32, Vec<u8>),
) -> ImageResult<()> {
OpenExrEncoder::new(write).write_image(data.as_slice(), *width, *height, ExtendedColorType::Rgba32F)
OpenExrEncoder::new(write).write_image(
data.as_slice(),
*width,
*height,
ExtendedColorType::Rgba32F,
)
}

let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;
Expand Down
13 changes: 7 additions & 6 deletions src/codecs/bmp/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cmp::{self, Ordering};
use std::io::{self, Read, Seek, SeekFrom};
use std::io::{self, BufRead, Seek, SeekFrom};
use std::iter::{repeat, Rev};
use std::slice::ChunksMut;
use std::{error, fmt};
Expand Down Expand Up @@ -502,7 +502,7 @@ enum RLEInsn {
PixelRun(u8, u8),
}

impl<R: Read + Seek> BmpDecoder<R> {
impl<R: BufRead + Seek> BmpDecoder<R> {
fn new_decoder(reader: R) -> BmpDecoder<R> {
BmpDecoder {
reader,
Expand Down Expand Up @@ -1329,7 +1329,7 @@ impl<R: Read + Seek> BmpDecoder<R> {
}
}

impl<R: Read + Seek> ImageDecoder for BmpDecoder<R> {
impl<R: BufRead + Seek> ImageDecoder for BmpDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width as u32, self.height as u32)
}
Expand All @@ -1354,7 +1354,7 @@ impl<R: Read + Seek> ImageDecoder for BmpDecoder<R> {
}
}

impl<R: Read + Seek> ImageDecoderRect for BmpDecoder<R> {
impl<R: BufRead + Seek> ImageDecoderRect for BmpDecoder<R> {
fn read_rect(
&mut self,
x: u32,
Expand Down Expand Up @@ -1384,7 +1384,7 @@ impl<R: Read + Seek> ImageDecoderRect for BmpDecoder<R> {

#[cfg(test)]
mod test {
use std::io::Cursor;
use std::io::{BufReader, Cursor};

use super::*;

Expand All @@ -1405,7 +1405,8 @@ mod test {

#[test]
fn read_rect() {
let f = std::fs::File::open("tests/images/bmp/images/Core_8_Bit.bmp").unwrap();
let f =
BufReader::new(std::fs::File::open("tests/images/bmp/images/Core_8_Bit.bmp").unwrap());
let mut decoder = super::BmpDecoder::new(f).unwrap();

let mut buf: Vec<u8> = vec![0; 8 * 8 * 3];
Expand Down
11 changes: 6 additions & 5 deletions src/codecs/gif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
//! use image::codecs::gif::{GifDecoder, GifEncoder};
//! use image::{ImageDecoder, AnimationDecoder};
//! use std::fs::File;
//! use std::io::BufReader;
//! # fn main() -> std::io::Result<()> {
//! // Decode a gif into frames
//! let file_in = File::open("foo.gif")?;
//! let file_in = BufReader::new(File::open("foo.gif")?);
//! let mut decoder = GifDecoder::new(file_in).unwrap();
//! let frames = decoder.into_frames();
//! let frames = frames.collect_frames().expect("error decoding gif");
Expand All @@ -26,7 +27,7 @@
//! ```
#![allow(clippy::while_let_loop)]

use std::io::{self, Cursor, Read, Write};
use std::io::{self, BufRead, Cursor, Read, Seek, Write};
use std::marker::PhantomData;
use std::mem;

Expand Down Expand Up @@ -82,7 +83,7 @@ impl<R> Read for GifReader<R> {
}
}

impl<R: Read> ImageDecoder for GifDecoder<R> {
impl<R: BufRead + Seek> ImageDecoder for GifDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(
u32::from(self.reader.width()),
Expand Down Expand Up @@ -226,7 +227,7 @@ struct GifFrameIterator<R: Read> {
limits: Limits,
}

impl<R: Read> GifFrameIterator<R> {
impl<R: BufRead + Seek> GifFrameIterator<R> {
fn new(decoder: GifDecoder<R>) -> GifFrameIterator<R> {
let (width, height) = decoder.dimensions();
let limits = decoder.limits.clone();
Expand Down Expand Up @@ -392,7 +393,7 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
}
}

impl<'a, R: Read + 'a> AnimationDecoder<'a> for GifDecoder<R> {
impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for GifDecoder<R> {
fn into_frames(self) -> animation::Frames<'a> {
animation::Frames::new(Box::new(GifFrameIterator::new(self)))
}
Expand Down
12 changes: 6 additions & 6 deletions src/codecs/ico/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{Read, Seek, SeekFrom};
use std::io::{BufRead, Read, Seek, SeekFrom};
use std::{error, fmt};

use crate::color::ColorType;
Expand Down Expand Up @@ -106,12 +106,12 @@ impl From<IcoEntryImageFormat> for ImageFormat {
}

/// An ico decoder
pub struct IcoDecoder<R: Read> {
pub struct IcoDecoder<R: BufRead + Seek> {
selected_entry: DirEntry,
inner_decoder: InnerDecoder<R>,
}

enum InnerDecoder<R: Read> {
enum InnerDecoder<R: BufRead + Seek> {
Bmp(BmpDecoder<R>),
Png(Box<PngDecoder<R>>),
}
Expand Down Expand Up @@ -141,7 +141,7 @@ struct DirEntry {
image_offset: u32,
}

impl<R: Read + Seek> IcoDecoder<R> {
impl<R: BufRead + Seek> IcoDecoder<R> {
/// Create a new decoder that decodes from the stream ```r```
pub fn new(mut r: R) -> ImageResult<IcoDecoder<R>> {
let entries = read_entries(&mut r)?;
Expand Down Expand Up @@ -248,7 +248,7 @@ impl DirEntry {
Ok(signature == PNG_SIGNATURE)
}

fn decoder<R: Read + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
fn decoder<R: BufRead + Seek>(&self, mut r: R) -> ImageResult<InnerDecoder<R>> {
let is_png = self.is_png(&mut r)?;
self.seek_to_start(&mut r)?;

Expand All @@ -260,7 +260,7 @@ impl DirEntry {
}
}

impl<R: Read + Seek> ImageDecoder for IcoDecoder<R> {
impl<R: BufRead + Seek> ImageDecoder for IcoDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
match self.inner_decoder {
Bmp(ref decoder) => decoder.dimensions(),
Expand Down
6 changes: 3 additions & 3 deletions src/codecs/jpeg/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io::Read;
use std::io::{BufRead, Seek};
use std::marker::PhantomData;

use crate::color::ColorType;
Expand All @@ -22,7 +22,7 @@ pub struct JpegDecoder<R> {
phantom: PhantomData<R>,
}

impl<R: Read> JpegDecoder<R> {
impl<R: BufRead + Seek> JpegDecoder<R> {
/// Create a new decoder that decodes from the stream ```r```
pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
let mut input = Vec::new();
Expand Down Expand Up @@ -50,7 +50,7 @@ impl<R: Read> JpegDecoder<R> {
}
}

impl<R: Read> ImageDecoder for JpegDecoder<R> {
impl<R: BufRead + Seek> ImageDecoder for JpegDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(u32::from(self.width), u32::from(self.height))
}
Expand Down
10 changes: 5 additions & 5 deletions src/codecs/openexr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
ColorType, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageFormat, ImageResult,
};

use std::io::{Read, Seek, Write};
use std::io::{BufRead, Seek, Write};

/// An OpenEXR decoder. Immediately reads the meta data from the file.
#[derive(Debug)]
Expand All @@ -45,7 +45,7 @@ pub struct OpenExrDecoder<R> {
alpha_present_in_file: bool,
}

impl<R: Read + Seek> OpenExrDecoder<R> {
impl<R: BufRead + Seek> OpenExrDecoder<R> {
/// Create a decoder. Consumes the first few bytes of the source to extract image dimensions.
/// Assumes the reader is buffered. In most cases,
/// you should wrap your reader in a `BufReader` for best performance.
Expand Down Expand Up @@ -104,7 +104,7 @@ impl<R: Read + Seek> OpenExrDecoder<R> {
}
}

impl<R: Read + Seek> ImageDecoder for OpenExrDecoder<R> {
impl<R: BufRead + Seek> ImageDecoder for OpenExrDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
let size = self
.selected_exr_header()
Expand Down Expand Up @@ -382,7 +382,7 @@ mod test {
}

/// Read the file from the specified path into an `Rgb32FImage`.
fn read_as_rgb_image(read: impl Read + Seek) -> ImageResult<Rgb32FImage> {
fn read_as_rgb_image(read: impl BufRead + Seek) -> ImageResult<Rgb32FImage> {
let decoder = OpenExrDecoder::with_alpha_preference(read, Some(false))?;
let (width, height) = decoder.dimensions();
let buffer: Vec<f32> = crate::image::decoder_to_vec(decoder)?;
Expand All @@ -396,7 +396,7 @@ mod test {
}

/// Read the file from the specified path into an `Rgba32FImage`.
fn read_as_rgba_image(read: impl Read + Seek) -> ImageResult<Rgba32FImage> {
fn read_as_rgba_image(read: impl BufRead + Seek) -> ImageResult<Rgba32FImage> {
let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
let (width, height) = decoder.dimensions();
let buffer: Vec<f32> = crate::image::decoder_to_vec(decoder)?;
Expand Down
26 changes: 13 additions & 13 deletions src/codecs/png.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//!
use std::fmt;
use std::io::{Read, Write};
use std::io::{BufRead, Seek, Write};

use png::{BlendOp, DisposeOp};

Expand All @@ -26,13 +26,13 @@ use crate::{DynamicImage, GenericImage, ImageBuffer, Luma, LumaA, Rgb, Rgba, Rgb
pub(crate) const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];

/// PNG decoder
pub struct PngDecoder<R: Read> {
pub struct PngDecoder<R: BufRead + Seek> {
color_type: ColorType,
reader: png::Reader<R>,
limits: Limits,
}

impl<R: Read> PngDecoder<R> {
impl<R: BufRead + Seek> PngDecoder<R> {
/// Creates a new decoder that decodes from the stream ```r```
pub fn new(r: R) -> ImageResult<PngDecoder<R>> {
Self::with_limits(r, Limits::no_limits())
Expand Down Expand Up @@ -164,7 +164,7 @@ fn unsupported_color(ect: ExtendedColorType) -> ImageError {
))
}

impl<R: Read> ImageDecoder for PngDecoder<R> {
impl<R: BufRead + Seek> ImageDecoder for PngDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
self.reader.info().size()
}
Expand Down Expand Up @@ -221,7 +221,7 @@ impl<R: Read> ImageDecoder for PngDecoder<R> {
/// [`AnimationDecoder`]: ../trait.AnimationDecoder.html
/// [`PngDecoder`]: struct.PngDecoder.html
/// [`PngDecoder::apng`]: struct.PngDecoder.html#method.apng
pub struct ApngDecoder<R: Read> {
pub struct ApngDecoder<R: BufRead + Seek> {
inner: PngDecoder<R>,
/// The current output buffer.
current: Option<RgbaImage>,
Expand All @@ -235,7 +235,7 @@ pub struct ApngDecoder<R: Read> {
has_thumbnail: bool,
}

impl<R: Read> ApngDecoder<R> {
impl<R: BufRead + Seek> ApngDecoder<R> {
fn new(inner: PngDecoder<R>) -> Self {
let info = inner.reader.info();
let remaining = match info.animation_control() {
Expand Down Expand Up @@ -419,11 +419,11 @@ impl<R: Read> ApngDecoder<R> {
}
}

impl<'a, R: Read + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
fn into_frames(self) -> Frames<'a> {
struct FrameIterator<R: Read>(ApngDecoder<R>);
struct FrameIterator<R: BufRead + Seek>(ApngDecoder<R>);

impl<R: Read> Iterator for FrameIterator<R> {
impl<R: BufRead + Seek> Iterator for FrameIterator<R> {
type Item = ImageResult<Frame>;

fn next(&mut self) -> Option<Self::Item> {
Expand Down Expand Up @@ -685,14 +685,14 @@ impl std::error::Error for BadPngRepresentation {}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
use std::io::{BufReader, Cursor, Read};

#[test]
fn ensure_no_decoder_off_by_one() {
let dec = PngDecoder::new(
let dec = PngDecoder::new(BufReader::new(
std::fs::File::open("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
.unwrap(),
)
))
.expect("Unable to read PNG file (does it exist?)");

assert_eq![(2000, 1000), dec.dimensions()];
Expand Down Expand Up @@ -721,7 +721,7 @@ mod tests {
.unwrap();
not_png[0] = 0;

let error = PngDecoder::new(&not_png[..]).err().unwrap();
let error = PngDecoder::new(Cursor::new(&not_png)).err().unwrap();
let _ = error
.source()
.unwrap()
Expand Down
8 changes: 4 additions & 4 deletions src/codecs/tga/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
image::{ImageDecoder, ImageFormat},
};
use byteorder::ReadBytesExt;
use std::io::{self, Read, Seek};
use std::io::{self, Read};

struct ColorMap {
/// sizes in bytes
Expand Down Expand Up @@ -59,7 +59,7 @@ pub struct TgaDecoder<R> {
color_map: Option<ColorMap>,
}

impl<R: Read + Seek> TgaDecoder<R> {
impl<R: Read> TgaDecoder<R> {
/// Create a new decoder that decodes from the stream `r`
pub fn new(r: R) -> ImageResult<TgaDecoder<R>> {
let mut decoder = TgaDecoder {
Expand Down Expand Up @@ -172,7 +172,7 @@ impl<R: Read + Seek> TgaDecoder<R> {
/// is present
fn read_image_id(&mut self) -> ImageResult<()> {
self.r
.seek(io::SeekFrom::Current(i64::from(self.header.id_length)))?;
.read_exact(&mut vec![0; self.header.id_length as usize])?;
Ok(())
}

Expand Down Expand Up @@ -336,7 +336,7 @@ impl<R: Read + Seek> TgaDecoder<R> {
}
}

impl<R: Read + Seek> ImageDecoder for TgaDecoder<R> {
impl<R: Read> ImageDecoder for TgaDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width as u32, self.height as u32)
}
Expand Down
Loading

0 comments on commit 0ed15a8

Please sign in to comment.