image/codecs/pnm/
decoder.rs

1use std::error;
2use std::fmt::{self, Display};
3use std::io::{self, Read};
4use std::mem::size_of;
5use std::num::ParseIntError;
6use std::str;
7
8use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
9use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
10use crate::color::{ColorType, ExtendedColorType};
11use crate::error::{
12    DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
13};
14use crate::image::{ImageDecoder, ImageFormat};
15use crate::utils;
16
17use byteorder_lite::{BigEndian, ByteOrder, NativeEndian};
18
19/// All errors that can occur when attempting to parse a PNM
20#[derive(Debug, Clone)]
21enum DecoderError {
22    /// PNM's "P[123456]" signature wrong or missing
23    PnmMagicInvalid([u8; 2]),
24    /// Couldn't parse the specified string as an integer from the specified source
25    UnparsableValue(ErrorDataSource, String, ParseIntError),
26
27    /// More than the exactly one allowed plane specified by the format
28    NonAsciiByteInHeader(u8),
29    /// The PAM header contained a non-ASCII byte
30    NonAsciiLineInPamHeader,
31    /// Couldn't parse an integer: expected but did not get an ASCII digit
32    InvalidDigit(ErrorDataSource),
33
34    /// The byte after the P7 magic was not 0x0A NEWLINE
35    NotNewlineAfterP7Magic(u8),
36    /// The PNM header had too few lines
37    UnexpectedPnmHeaderEnd,
38
39    /// The specified line was specified twice
40    HeaderLineDuplicated(PnmHeaderLine),
41    /// The line with the specified ID was not understood
42    HeaderLineUnknown(String),
43    /// At least one of the required lines were missing from the header (are `None` here)
44    ///
45    /// Same names as [`PnmHeaderLine`](enum.PnmHeaderLine.html)
46    #[allow(missing_docs)]
47    HeaderLineMissing {
48        height: Option<u32>,
49        width: Option<u32>,
50        depth: Option<u32>,
51        maxval: Option<u32>,
52    },
53
54    /// Not enough data was provided to the Decoder to decode the image
55    InputTooShort,
56    /// Sample raster contained unexpected byte
57    UnexpectedByteInRaster(u8),
58    /// Specified sample was out of bounds (e.g. >1 in B&W)
59    SampleOutOfBounds(u8),
60    /// The image's maxval is zero
61    MaxvalZero,
62    /// The image's maxval exceeds 0xFFFF
63    MaxvalTooBig(u32),
64
65    /// The specified tuple type supports restricted depths and maxvals, those restrictions were not met
66    InvalidDepthOrMaxval {
67        tuple_type: ArbitraryTuplType,
68        depth: u32,
69        maxval: u32,
70    },
71    /// The specified tuple type supports restricted depths, those restrictions were not met
72    InvalidDepth {
73        tuple_type: ArbitraryTuplType,
74        depth: u32,
75    },
76    /// The tuple type was not recognised by the parser
77    TupleTypeUnrecognised,
78
79    /// Overflowed the specified value when parsing
80    Overflow,
81}
82
83impl Display for DecoderError {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            DecoderError::PnmMagicInvalid(magic) => f.write_fmt(format_args!(
87                "Expected magic constant for PNM: P1..P7, got [{:#04X?}, {:#04X?}]",
88                magic[0], magic[1]
89            )),
90            DecoderError::UnparsableValue(src, data, err) => {
91                f.write_fmt(format_args!("Error parsing {data:?} as {src}: {err}"))
92            }
93
94            DecoderError::NonAsciiByteInHeader(c) => {
95                f.write_fmt(format_args!("Non-ASCII character {c:#04X?} in header"))
96            }
97            DecoderError::NonAsciiLineInPamHeader => f.write_str("Non-ASCII line in PAM header"),
98            DecoderError::InvalidDigit(src) => {
99                f.write_fmt(format_args!("Non-ASCII-digit character when parsing number in {src}"))
100            }
101
102            DecoderError::NotNewlineAfterP7Magic(c) => f.write_fmt(format_args!(
103                "Expected newline after P7 magic, got {c:#04X?}"
104            )),
105            DecoderError::UnexpectedPnmHeaderEnd => f.write_str("Unexpected end of PNM header"),
106
107            DecoderError::HeaderLineDuplicated(line) => {
108                f.write_fmt(format_args!("Duplicate {line} line"))
109            }
110            DecoderError::HeaderLineUnknown(identifier) => f.write_fmt(format_args!(
111                "Unknown header line with identifier {identifier:?}"
112            )),
113            DecoderError::HeaderLineMissing {
114                height,
115                width,
116                depth,
117                maxval,
118            } => f.write_fmt(format_args!(
119                "Missing header line: have height={height:?}, width={width:?}, depth={depth:?}, maxval={maxval:?}"
120            )),
121
122            DecoderError::InputTooShort => {
123                f.write_str("Not enough data was provided to the Decoder to decode the image")
124            }
125            DecoderError::UnexpectedByteInRaster(c) => f.write_fmt(format_args!(
126                "Unexpected character {c:#04X?} within sample raster"
127            )),
128            DecoderError::SampleOutOfBounds(val) => {
129                f.write_fmt(format_args!("Sample value {val} outside of bounds"))
130            }
131            DecoderError::MaxvalZero => f.write_str("Image MAXVAL is zero"),
132            DecoderError::MaxvalTooBig(maxval) => {
133                f.write_fmt(format_args!("Image MAXVAL exceeds {}: {}", 0xFFFF, maxval))
134            }
135
136            DecoderError::InvalidDepthOrMaxval {
137                tuple_type,
138                depth,
139                maxval,
140            } => f.write_fmt(format_args!(
141                "Invalid depth ({}) or maxval ({}) for tuple type {}",
142                depth,
143                maxval,
144                tuple_type.name()
145            )),
146            DecoderError::InvalidDepth { tuple_type, depth } => f.write_fmt(format_args!(
147                "Invalid depth ({}) for tuple type {}",
148                depth,
149                tuple_type.name()
150            )),
151            DecoderError::TupleTypeUnrecognised => f.write_str("Tuple type not recognized"),
152            DecoderError::Overflow => f.write_str("Overflow when parsing value"),
153        }
154    }
155}
156
157/// Note: should `pnm` be extracted into a separate crate,
158/// this will need to be hidden until that crate hits version `1.0`.
159impl From<DecoderError> for ImageError {
160    fn from(e: DecoderError) -> ImageError {
161        ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e))
162    }
163}
164
165impl error::Error for DecoderError {
166    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
167        match self {
168            DecoderError::UnparsableValue(_, _, err) => Some(err),
169            _ => None,
170        }
171    }
172}
173
174/// Single-value lines in a PNM header
175#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
176enum PnmHeaderLine {
177    /// "HEIGHT"
178    Height,
179    /// "WIDTH"
180    Width,
181    /// "DEPTH"
182    Depth,
183    /// "MAXVAL", a.k.a. `maxwhite`
184    Maxval,
185}
186
187impl Display for PnmHeaderLine {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        f.write_str(match self {
190            PnmHeaderLine::Height => "HEIGHT",
191            PnmHeaderLine::Width => "WIDTH",
192            PnmHeaderLine::Depth => "DEPTH",
193            PnmHeaderLine::Maxval => "MAXVAL",
194        })
195    }
196}
197
198/// Single-value lines in a PNM header
199#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
200enum ErrorDataSource {
201    /// One of the header lines
202    Line(PnmHeaderLine),
203    /// Value in the preamble
204    Preamble,
205    /// Sample/pixel data
206    Sample,
207}
208
209impl Display for ErrorDataSource {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        match self {
212            ErrorDataSource::Line(l) => l.fmt(f),
213            ErrorDataSource::Preamble => f.write_str("number in preamble"),
214            ErrorDataSource::Sample => f.write_str("sample"),
215        }
216    }
217}
218
219/// Dynamic representation, represents all decodable (sample, depth) combinations.
220#[derive(Clone, Copy)]
221enum TupleType {
222    PbmBit,
223    BWBit,
224    GrayU8,
225    GrayU16,
226    RGBU8,
227    RGBU16,
228}
229
230trait Sample {
231    type Representation;
232
233    /// Representation size in bytes
234    fn sample_size() -> u32 {
235        size_of::<Self::Representation>() as u32
236    }
237    fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
238        Ok((width * height * samples * Self::sample_size()) as usize)
239    }
240    fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()>;
241    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>;
242}
243
244struct U8;
245struct U16;
246struct PbmBit;
247struct BWBit;
248
249trait DecodableImageHeader {
250    fn tuple_type(&self) -> ImageResult<TupleType>;
251}
252
253/// PNM decoder
254pub struct PnmDecoder<R> {
255    reader: R,
256    header: PnmHeader,
257    tuple: TupleType,
258}
259
260impl<R: Read> PnmDecoder<R> {
261    /// Create a new decoder that decodes from the stream ```read```
262    pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> {
263        let magic = buffered_read.read_magic_constant()?;
264
265        let subtype = match magic {
266            [b'P', b'1'] => PnmSubtype::Bitmap(SampleEncoding::Ascii),
267            [b'P', b'2'] => PnmSubtype::Graymap(SampleEncoding::Ascii),
268            [b'P', b'3'] => PnmSubtype::Pixmap(SampleEncoding::Ascii),
269            [b'P', b'4'] => PnmSubtype::Bitmap(SampleEncoding::Binary),
270            [b'P', b'5'] => PnmSubtype::Graymap(SampleEncoding::Binary),
271            [b'P', b'6'] => PnmSubtype::Pixmap(SampleEncoding::Binary),
272            [b'P', b'7'] => PnmSubtype::ArbitraryMap,
273            _ => return Err(DecoderError::PnmMagicInvalid(magic).into()),
274        };
275
276        let decoder = match subtype {
277            PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc),
278            PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc),
279            PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc),
280            PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read),
281        }?;
282
283        if utils::check_dimension_overflow(
284            decoder.dimensions().0,
285            decoder.dimensions().1,
286            decoder.color_type().bytes_per_pixel(),
287        ) {
288            return Err(ImageError::Unsupported(
289                UnsupportedError::from_format_and_kind(
290                    ImageFormat::Pnm.into(),
291                    UnsupportedErrorKind::GenericFeature(format!(
292                        "Image dimensions ({}x{}) are too large",
293                        decoder.dimensions().0,
294                        decoder.dimensions().1
295                    )),
296                ),
297            ));
298        }
299
300        Ok(decoder)
301    }
302
303    /// Extract the reader and header after an image has been read.
304    pub fn into_inner(self) -> (R, PnmHeader) {
305        (self.reader, self.header)
306    }
307
308    fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
309        let header = reader.read_bitmap_header(encoding)?;
310        Ok(PnmDecoder {
311            reader,
312            tuple: TupleType::PbmBit,
313            header: PnmHeader {
314                decoded: HeaderRecord::Bitmap(header),
315                encoded: None,
316            },
317        })
318    }
319
320    fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
321        let header = reader.read_graymap_header(encoding)?;
322        let tuple_type = header.tuple_type()?;
323        Ok(PnmDecoder {
324            reader,
325            tuple: tuple_type,
326            header: PnmHeader {
327                decoded: HeaderRecord::Graymap(header),
328                encoded: None,
329            },
330        })
331    }
332
333    fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
334        let header = reader.read_pixmap_header(encoding)?;
335        let tuple_type = header.tuple_type()?;
336        Ok(PnmDecoder {
337            reader,
338            tuple: tuple_type,
339            header: PnmHeader {
340                decoded: HeaderRecord::Pixmap(header),
341                encoded: None,
342            },
343        })
344    }
345
346    fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> {
347        let header = reader.read_arbitrary_header()?;
348        let tuple_type = header.tuple_type()?;
349        Ok(PnmDecoder {
350            reader,
351            tuple: tuple_type,
352            header: PnmHeader {
353                decoded: HeaderRecord::Arbitrary(header),
354                encoded: None,
355            },
356        })
357    }
358}
359
360trait HeaderReader: Read {
361    /// Reads the two magic constant bytes
362    fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> {
363        let mut magic: [u8; 2] = [0, 0];
364        self.read_exact(&mut magic)?;
365        Ok(magic)
366    }
367
368    /// Reads a string as well as a single whitespace after it, ignoring comments
369    fn read_next_string(&mut self) -> ImageResult<String> {
370        let mut bytes = Vec::new();
371
372        // pair input bytes with a bool mask to remove comments
373        #[allow(clippy::unbuffered_bytes)]
374        let mark_comments = self.bytes().scan(true, |partof, read| {
375            let byte = match read {
376                Err(err) => return Some((*partof, Err(err))),
377                Ok(byte) => byte,
378            };
379            let cur_enabled = *partof && byte != b'#';
380            let next_enabled = cur_enabled || (byte == b'\r' || byte == b'\n');
381            *partof = next_enabled;
382            Some((cur_enabled, Ok(byte)))
383        });
384
385        for (_, byte) in mark_comments.filter(|e| e.0) {
386            match byte {
387                Ok(b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ') => {
388                    if !bytes.is_empty() {
389                        break; // We're done as we already have some content
390                    }
391                }
392                Ok(byte) if !byte.is_ascii() => {
393                    return Err(DecoderError::NonAsciiByteInHeader(byte).into())
394                }
395                Ok(byte) => {
396                    bytes.push(byte);
397                }
398                Err(_) => break,
399            }
400        }
401
402        if bytes.is_empty() {
403            return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into()));
404        }
405
406        if !bytes.as_slice().is_ascii() {
407            // We have only filled the buffer with characters for which `byte.is_ascii()` holds.
408            unreachable!("Non-ASCII character should have returned sooner")
409        }
410
411        let string = String::from_utf8(bytes)
412            // We checked the precondition ourselves a few lines before, `bytes.as_slice().is_ascii()`.
413            .unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded"));
414
415        Ok(string)
416    }
417
418    fn read_next_line(&mut self) -> ImageResult<String> {
419        let mut buffer = Vec::new();
420        loop {
421            let mut byte = [0];
422            if self.read(&mut byte)? == 0 || byte[0] == b'\n' {
423                break;
424            }
425            buffer.push(byte[0]);
426        }
427
428        String::from_utf8(buffer)
429            .map_err(|e| ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e)))
430    }
431
432    fn read_next_u32(&mut self) -> ImageResult<u32> {
433        let s = self.read_next_string()?;
434        s.parse::<u32>()
435            .map_err(|err| DecoderError::UnparsableValue(ErrorDataSource::Preamble, s, err).into())
436    }
437
438    fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> {
439        let width = self.read_next_u32()?;
440        let height = self.read_next_u32()?;
441        Ok(BitmapHeader {
442            encoding,
443            height,
444            width,
445        })
446    }
447
448    fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> {
449        self.read_pixmap_header(encoding).map(
450            |PixmapHeader {
451                 encoding,
452                 width,
453                 height,
454                 maxval,
455             }| GraymapHeader {
456                encoding,
457                width,
458                height,
459                maxwhite: maxval,
460            },
461        )
462    }
463
464    fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> {
465        let width = self.read_next_u32()?;
466        let height = self.read_next_u32()?;
467        let maxval = self.read_next_u32()?;
468        Ok(PixmapHeader {
469            encoding,
470            height,
471            width,
472            maxval,
473        })
474    }
475
476    fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> {
477        fn parse_single_value_line(
478            line_val: &mut Option<u32>,
479            rest: &str,
480            line: PnmHeaderLine,
481        ) -> ImageResult<()> {
482            if line_val.is_some() {
483                Err(DecoderError::HeaderLineDuplicated(line).into())
484            } else {
485                let v = rest.trim().parse().map_err(|err| {
486                    DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err)
487                })?;
488                *line_val = Some(v);
489                Ok(())
490            }
491        }
492
493        #[allow(clippy::unbuffered_bytes)]
494        match self.bytes().next() {
495            None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())),
496            Some(Err(io)) => return Err(ImageError::IoError(io)),
497            Some(Ok(b'\n')) => (),
498            Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()),
499        }
500
501        let mut line;
502        let mut height: Option<u32> = None;
503        let mut width: Option<u32> = None;
504        let mut depth: Option<u32> = None;
505        let mut maxval: Option<u32> = None;
506        let mut tupltype: Option<String> = None;
507        loop {
508            line = self.read_next_line()?;
509            if line.is_empty() {
510                return Err(DecoderError::UnexpectedPnmHeaderEnd.into());
511            }
512            if line.as_bytes()[0] == b'#' {
513                continue;
514            }
515            if !line.is_ascii() {
516                return Err(DecoderError::NonAsciiLineInPamHeader.into());
517            }
518            #[allow(deprecated)]
519            let (identifier, rest) = line
520                .trim_left()
521                .split_at(line.find(char::is_whitespace).unwrap_or(line.len()));
522            match identifier {
523                "ENDHDR" => break,
524                "HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?,
525                "WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?,
526                "DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?,
527                "MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?,
528                "TUPLTYPE" => {
529                    let identifier = rest.trim();
530                    if tupltype.is_some() {
531                        let appended = tupltype.take().map(|mut v| {
532                            v.push(' ');
533                            v.push_str(identifier);
534                            v
535                        });
536                        tupltype = appended;
537                    } else {
538                        tupltype = Some(identifier.to_string());
539                    }
540                }
541                _ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()),
542            }
543        }
544
545        let (Some(h), Some(w), Some(d), Some(m)) = (height, width, depth, maxval) else {
546            return Err(DecoderError::HeaderLineMissing {
547                height,
548                width,
549                depth,
550                maxval,
551            }
552            .into());
553        };
554
555        let tupltype = match tupltype {
556            None => None,
557            Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite),
558            Some(ref t) if t == "BLACKANDWHITE_ALPHA" => {
559                Some(ArbitraryTuplType::BlackAndWhiteAlpha)
560            }
561            Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale),
562            Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha),
563            Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB),
564            Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha),
565            Some(other) => Some(ArbitraryTuplType::Custom(other)),
566        };
567
568        Ok(ArbitraryHeader {
569            height: h,
570            width: w,
571            depth: d,
572            maxval: m,
573            tupltype,
574        })
575    }
576}
577
578impl<R> HeaderReader for R where R: Read {}
579
580impl<R: Read> ImageDecoder for PnmDecoder<R> {
581    fn dimensions(&self) -> (u32, u32) {
582        (self.header.width(), self.header.height())
583    }
584
585    fn color_type(&self) -> ColorType {
586        match self.tuple {
587            TupleType::PbmBit => ColorType::L8,
588            TupleType::BWBit => ColorType::L8,
589            TupleType::GrayU8 => ColorType::L8,
590            TupleType::GrayU16 => ColorType::L16,
591            TupleType::RGBU8 => ColorType::Rgb8,
592            TupleType::RGBU16 => ColorType::Rgb16,
593        }
594    }
595
596    fn original_color_type(&self) -> ExtendedColorType {
597        match self.tuple {
598            TupleType::PbmBit => ExtendedColorType::L1,
599            TupleType::BWBit => ExtendedColorType::L1,
600            TupleType::GrayU8 => ExtendedColorType::L8,
601            TupleType::GrayU16 => ExtendedColorType::L16,
602            TupleType::RGBU8 => ExtendedColorType::Rgb8,
603            TupleType::RGBU16 => ExtendedColorType::Rgb16,
604        }
605    }
606
607    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
608        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
609        match self.tuple {
610            TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf),
611            TupleType::BWBit => self.read_samples::<BWBit>(1, buf),
612            TupleType::RGBU8 => self.read_samples::<U8>(3, buf),
613            TupleType::RGBU16 => self.read_samples::<U16>(3, buf),
614            TupleType::GrayU8 => self.read_samples::<U8>(1, buf),
615            TupleType::GrayU16 => self.read_samples::<U16>(1, buf),
616        }
617    }
618
619    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
620        (*self).read_image(buf)
621    }
622}
623
624impl<R: Read> PnmDecoder<R> {
625    fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> {
626        match self.subtype().sample_encoding() {
627            SampleEncoding::Binary => {
628                let width = self.header.width();
629                let height = self.header.height();
630                let bytecount = S::bytelen(width, height, components)?;
631
632                let mut bytes = vec![];
633                self.reader
634                    .by_ref()
635                    // This conversion is potentially lossy but unlikely and in that case we error
636                    // later anyways.
637                    .take(bytecount as u64)
638                    .read_to_end(&mut bytes)?;
639                if bytes.len() != bytecount {
640                    return Err(DecoderError::InputTooShort.into());
641                }
642
643                let width: usize = width.try_into().map_err(|_| DecoderError::Overflow)?;
644                let components: usize =
645                    components.try_into().map_err(|_| DecoderError::Overflow)?;
646                let row_size = width
647                    .checked_mul(components)
648                    .ok_or(DecoderError::Overflow)?;
649
650                S::from_bytes(&bytes, row_size, buf)?;
651            }
652            SampleEncoding::Ascii => {
653                self.read_ascii::<S>(buf)?;
654            }
655        };
656
657        // Scale samples if 8bit or 16bit is not saturated
658        let current_sample_max = self.header.maximal_sample();
659        let target_sample_max = 256_u32.pow(S::sample_size()) - 1;
660
661        if current_sample_max != target_sample_max {
662            let factor = target_sample_max as f32 / current_sample_max as f32;
663
664            if S::sample_size() == 1 {
665                for v in buf.iter_mut() {
666                    *v = (f32::from(*v) * factor).round() as u8;
667                }
668            } else if S::sample_size() == 2 {
669                for chunk in buf.chunks_exact_mut(2) {
670                    let v = NativeEndian::read_u16(chunk);
671                    NativeEndian::write_u16(chunk, (f32::from(v) * factor).round() as u16);
672                }
673            }
674        }
675
676        Ok(())
677    }
678
679    fn read_ascii<Basic: Sample>(&mut self, output_buf: &mut [u8]) -> ImageResult<()> {
680        Basic::from_ascii(&mut self.reader, output_buf)
681    }
682
683    /// Get the pnm subtype, depending on the magic constant contained in the header
684    pub fn subtype(&self) -> PnmSubtype {
685        self.header.subtype()
686    }
687}
688
689fn read_separated_ascii<T: TryFrom<u16>>(reader: &mut dyn Read) -> ImageResult<T> {
690    let is_separator = |v: &u8| matches!(*v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ');
691
692    let mut v: u16 = 0;
693    #[allow(clippy::unbuffered_bytes)]
694    for rc in reader
695        .bytes()
696        .skip_while(|v| v.as_ref().ok().is_some_and(is_separator))
697        .take_while(|v| v.as_ref().ok().is_some_and(|c| !is_separator(c)))
698    {
699        let c = rc?;
700        let digit = match c {
701            b'0'..=b'9' => (c - b'0') as u16,
702            _ => return Err(DecoderError::InvalidDigit(ErrorDataSource::Sample).into()),
703        };
704        v = v.checked_mul(10).ok_or(DecoderError::Overflow)?;
705        v = v.checked_add(digit).ok_or(DecoderError::Overflow)?;
706    }
707    Ok(T::try_from(v).or(Err(DecoderError::Overflow))?)
708}
709
710impl Sample for U8 {
711    type Representation = u8;
712
713    fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
714        output_buf.copy_from_slice(bytes);
715        Ok(())
716    }
717
718    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
719        for b in output_buf {
720            *b = read_separated_ascii(reader)?;
721        }
722        Ok(())
723    }
724}
725
726impl Sample for U16 {
727    type Representation = u16;
728
729    fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
730        output_buf.copy_from_slice(bytes);
731        for chunk in output_buf.chunks_exact_mut(2) {
732            let v = BigEndian::read_u16(chunk);
733            NativeEndian::write_u16(chunk, v);
734        }
735        Ok(())
736    }
737
738    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
739        for chunk in output_buf.chunks_exact_mut(2) {
740            let v = read_separated_ascii::<u16>(reader)?;
741            NativeEndian::write_u16(chunk, v);
742        }
743        Ok(())
744    }
745}
746
747// The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should
748// be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will
749// need to be reversed for the grayscale output.
750impl Sample for PbmBit {
751    type Representation = u8;
752
753    fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
754        let count = width * samples;
755        let linelen = (count / 8) + u32::from((count % 8) != 0);
756        Ok((linelen * height) as usize)
757    }
758
759    fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
760        let mut expanded = utils::expand_bits(1, row_size.try_into().unwrap(), bytes);
761        for b in &mut expanded {
762            *b = !*b;
763        }
764        output_buf.copy_from_slice(&expanded);
765        Ok(())
766    }
767
768    fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
769        #[allow(clippy::unbuffered_bytes)]
770        let mut bytes = reader.bytes();
771        for b in output_buf {
772            loop {
773                let byte = bytes
774                    .next()
775                    .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
776                match byte {
777                    b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
778                    b'0' => *b = 255,
779                    b'1' => *b = 0,
780                    c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
781                }
782                break;
783            }
784        }
785
786        Ok(())
787    }
788}
789
790// Encoded just like a normal U8 but we check the values.
791impl Sample for BWBit {
792    type Representation = u8;
793
794    fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
795        U8::from_bytes(bytes, row_size, output_buf)?;
796        if let Some(val) = output_buf.iter().find(|&val| *val > 1) {
797            return Err(DecoderError::SampleOutOfBounds(*val).into());
798        }
799        Ok(())
800    }
801
802    fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
803        unreachable!("BW bits from anymaps are never encoded as ASCII")
804    }
805}
806
807impl DecodableImageHeader for BitmapHeader {
808    fn tuple_type(&self) -> ImageResult<TupleType> {
809        Ok(TupleType::PbmBit)
810    }
811}
812
813impl DecodableImageHeader for GraymapHeader {
814    fn tuple_type(&self) -> ImageResult<TupleType> {
815        match self.maxwhite {
816            0 => Err(DecoderError::MaxvalZero.into()),
817            v if v <= 0xFF => Ok(TupleType::GrayU8),
818            v if v <= 0xFFFF => Ok(TupleType::GrayU16),
819            _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
820        }
821    }
822}
823
824impl DecodableImageHeader for PixmapHeader {
825    fn tuple_type(&self) -> ImageResult<TupleType> {
826        match self.maxval {
827            0 => Err(DecoderError::MaxvalZero.into()),
828            v if v <= 0xFF => Ok(TupleType::RGBU8),
829            v if v <= 0xFFFF => Ok(TupleType::RGBU16),
830            _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
831        }
832    }
833}
834
835impl DecodableImageHeader for ArbitraryHeader {
836    fn tuple_type(&self) -> ImageResult<TupleType> {
837        match self.tupltype {
838            _ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()),
839            None if self.depth == 1 => Ok(TupleType::GrayU8),
840            None if self.depth == 2 => Err(ImageError::Unsupported(
841                UnsupportedError::from_format_and_kind(
842                    ImageFormat::Pnm.into(),
843                    UnsupportedErrorKind::Color(ExtendedColorType::La8),
844                ),
845            )),
846            None if self.depth == 3 => Ok(TupleType::RGBU8),
847            None if self.depth == 4 => Err(ImageError::Unsupported(
848                UnsupportedError::from_format_and_kind(
849                    ImageFormat::Pnm.into(),
850                    UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
851                ),
852            )),
853
854            Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
855                Ok(TupleType::BWBit)
856            }
857            Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
858                tuple_type: ArbitraryTuplType::BlackAndWhite,
859                maxval: self.maxval,
860                depth: self.depth,
861            }
862            .into()),
863
864            Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
865                Ok(TupleType::GrayU8)
866            }
867            Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
868                Ok(TupleType::GrayU16)
869            }
870            Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
871                tuple_type: ArbitraryTuplType::Grayscale,
872                maxval: self.maxval,
873                depth: self.depth,
874            }
875            .into()),
876
877            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
878                Ok(TupleType::RGBU8)
879            }
880            Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
881                Ok(TupleType::RGBU16)
882            }
883            Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
884                tuple_type: ArbitraryTuplType::RGB,
885                depth: self.depth,
886            }
887            .into()),
888
889            Some(ArbitraryTuplType::BlackAndWhiteAlpha) => Err(ImageError::Unsupported(
890                UnsupportedError::from_format_and_kind(
891                    ImageFormat::Pnm.into(),
892                    UnsupportedErrorKind::GenericFeature(format!(
893                        "Color type {}",
894                        ArbitraryTuplType::BlackAndWhiteAlpha.name()
895                    )),
896                ),
897            )),
898            Some(ArbitraryTuplType::GrayscaleAlpha) => Err(ImageError::Unsupported(
899                UnsupportedError::from_format_and_kind(
900                    ImageFormat::Pnm.into(),
901                    UnsupportedErrorKind::Color(ExtendedColorType::La8),
902                ),
903            )),
904            Some(ArbitraryTuplType::RGBAlpha) => Err(ImageError::Unsupported(
905                UnsupportedError::from_format_and_kind(
906                    ImageFormat::Pnm.into(),
907                    UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
908                ),
909            )),
910            Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
911                UnsupportedError::from_format_and_kind(
912                    ImageFormat::Pnm.into(),
913                    UnsupportedErrorKind::GenericFeature(format!("Tuple type {custom:?}")),
914                ),
915            )),
916            None => Err(DecoderError::TupleTypeUnrecognised.into()),
917        }
918    }
919}
920
921#[cfg(test)]
922mod tests {
923    use super::*;
924    /// Tests reading of a valid blackandwhite pam
925    #[test]
926    fn pam_blackandwhite() {
927        let pamdata = b"P7
928WIDTH 4
929HEIGHT 4
930DEPTH 1
931MAXVAL 1
932TUPLTYPE BLACKANDWHITE
933# Comment line
934ENDHDR
935\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
936        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
937        assert_eq!(decoder.color_type(), ColorType::L8);
938        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
939        assert_eq!(decoder.dimensions(), (4, 4));
940        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
941
942        let mut image = vec![0; decoder.total_bytes() as usize];
943        decoder.read_image(&mut image).unwrap();
944        assert_eq!(
945            image,
946            vec![
947                0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00,
948                0x00, 0xFF
949            ]
950        );
951        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
952            (
953                _,
954                PnmHeader {
955                    decoded:
956                        HeaderRecord::Arbitrary(ArbitraryHeader {
957                            width: 4,
958                            height: 4,
959                            maxval: 1,
960                            depth: 1,
961                            tupltype: Some(ArbitraryTuplType::BlackAndWhite),
962                        }),
963                    encoded: _,
964                },
965            ) => (),
966            _ => panic!("Decoded header is incorrect"),
967        }
968    }
969
970    /// Tests reading of a valid grayscale pam
971    #[test]
972    fn pam_grayscale() {
973        let pamdata = b"P7
974WIDTH 4
975HEIGHT 4
976DEPTH 1
977MAXVAL 255
978TUPLTYPE GRAYSCALE
979# Comment line
980ENDHDR
981\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
982        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
983        assert_eq!(decoder.color_type(), ColorType::L8);
984        assert_eq!(decoder.dimensions(), (4, 4));
985        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
986
987        let mut image = vec![0; decoder.total_bytes() as usize];
988        decoder.read_image(&mut image).unwrap();
989        assert_eq!(
990            image,
991            vec![
992                0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
993                0xbe, 0xef
994            ]
995        );
996        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
997            (
998                _,
999                PnmHeader {
1000                    decoded:
1001                        HeaderRecord::Arbitrary(ArbitraryHeader {
1002                            width: 4,
1003                            height: 4,
1004                            depth: 1,
1005                            maxval: 255,
1006                            tupltype: Some(ArbitraryTuplType::Grayscale),
1007                        }),
1008                    encoded: _,
1009                },
1010            ) => (),
1011            _ => panic!("Decoded header is incorrect"),
1012        }
1013    }
1014
1015    /// Tests reading of a valid rgb pam
1016    #[test]
1017    fn pam_rgb() {
1018        let pamdata = b"P7
1019# Comment line
1020MAXVAL 255
1021TUPLTYPE RGB
1022DEPTH 3
1023WIDTH 2
1024HEIGHT 2
1025ENDHDR
1026\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1027        let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
1028        assert_eq!(decoder.color_type(), ColorType::Rgb8);
1029        assert_eq!(decoder.dimensions(), (2, 2));
1030        assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
1031
1032        let mut image = vec![0; decoder.total_bytes() as usize];
1033        decoder.read_image(&mut image).unwrap();
1034        assert_eq!(
1035            image,
1036            vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
1037        );
1038        match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
1039            (
1040                _,
1041                PnmHeader {
1042                    decoded:
1043                        HeaderRecord::Arbitrary(ArbitraryHeader {
1044                            maxval: 255,
1045                            tupltype: Some(ArbitraryTuplType::RGB),
1046                            depth: 3,
1047                            width: 2,
1048                            height: 2,
1049                        }),
1050                    encoded: _,
1051                },
1052            ) => (),
1053            _ => panic!("Decoded header is incorrect"),
1054        }
1055    }
1056
1057    #[test]
1058    fn pbm_binary() {
1059        // The data contains two rows of the image (each line is padded to the full byte). For
1060        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1061        let pbmbinary = [&b"P4 6 2\n"[..], &[0b0110_1100_u8, 0b1011_0111]].concat();
1062        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1063        assert_eq!(decoder.color_type(), ColorType::L8);
1064        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1065        assert_eq!(decoder.dimensions(), (6, 2));
1066        assert_eq!(
1067            decoder.subtype(),
1068            PnmSubtype::Bitmap(SampleEncoding::Binary)
1069        );
1070        let mut image = vec![0; decoder.total_bytes() as usize];
1071        decoder.read_image(&mut image).unwrap();
1072        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1073        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1074            (
1075                _,
1076                PnmHeader {
1077                    decoded:
1078                        HeaderRecord::Bitmap(BitmapHeader {
1079                            encoding: SampleEncoding::Binary,
1080                            width: 6,
1081                            height: 2,
1082                        }),
1083                    encoded: _,
1084                },
1085            ) => (),
1086            _ => panic!("Decoded header is incorrect"),
1087        }
1088    }
1089
1090    /// A previous infinite loop.
1091    #[test]
1092    fn pbm_binary_ascii_termination() {
1093        use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
1094        struct FailRead(Cursor<&'static [u8]>);
1095
1096        impl Read for FailRead {
1097            fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
1098                match self.0.read(buf) {
1099                    Ok(n) if n > 0 => Ok(n),
1100                    _ => Err(Error::new(
1101                        ErrorKind::BrokenPipe,
1102                        "Simulated broken pipe error",
1103                    )),
1104                }
1105            }
1106        }
1107
1108        let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
1109
1110        let decoder = PnmDecoder::new(pbmbinary).unwrap();
1111        let mut image = vec![0; decoder.total_bytes() as usize];
1112        decoder
1113            .read_image(&mut image)
1114            .expect_err("Image is malformed");
1115    }
1116
1117    #[test]
1118    fn pbm_ascii() {
1119        // The data contains two rows of the image (each line is padded to the full byte). For
1120        // comments on its format, see documentation of `impl SampleType for PbmBit`.  Tests all
1121        // whitespace characters that should be allowed (the 6 characters according to POSIX).
1122        let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
1123        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1124        assert_eq!(decoder.color_type(), ColorType::L8);
1125        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1126        assert_eq!(decoder.dimensions(), (6, 2));
1127        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1128
1129        let mut image = vec![0; decoder.total_bytes() as usize];
1130        decoder.read_image(&mut image).unwrap();
1131        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1132        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1133            (
1134                _,
1135                PnmHeader {
1136                    decoded:
1137                        HeaderRecord::Bitmap(BitmapHeader {
1138                            encoding: SampleEncoding::Ascii,
1139                            width: 6,
1140                            height: 2,
1141                        }),
1142                    encoded: _,
1143                },
1144            ) => (),
1145            _ => panic!("Decoded header is incorrect"),
1146        }
1147    }
1148
1149    #[test]
1150    fn pbm_ascii_nospace() {
1151        // The data contains two rows of the image (each line is padded to the full byte). Notably,
1152        // it is completely within specification for the ascii data not to contain separating
1153        // whitespace for the pbm format or any mix.
1154        let pbmbinary = b"P1 6 2\n011011101101";
1155        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1156        assert_eq!(decoder.color_type(), ColorType::L8);
1157        assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
1158        assert_eq!(decoder.dimensions(), (6, 2));
1159        assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
1160
1161        let mut image = vec![0; decoder.total_bytes() as usize];
1162        decoder.read_image(&mut image).unwrap();
1163        assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
1164        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1165            (
1166                _,
1167                PnmHeader {
1168                    decoded:
1169                        HeaderRecord::Bitmap(BitmapHeader {
1170                            encoding: SampleEncoding::Ascii,
1171                            width: 6,
1172                            height: 2,
1173                        }),
1174                    encoded: _,
1175                },
1176            ) => (),
1177            _ => panic!("Decoded header is incorrect"),
1178        }
1179    }
1180
1181    #[test]
1182    fn pgm_binary() {
1183        // The data contains two rows of the image (each line is padded to the full byte). For
1184        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1185        let elements = (0..16).collect::<Vec<_>>();
1186        let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
1187        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1188        assert_eq!(decoder.color_type(), ColorType::L8);
1189        assert_eq!(decoder.dimensions(), (4, 4));
1190        assert_eq!(
1191            decoder.subtype(),
1192            PnmSubtype::Graymap(SampleEncoding::Binary)
1193        );
1194        let mut image = vec![0; decoder.total_bytes() as usize];
1195        decoder.read_image(&mut image).unwrap();
1196        assert_eq!(image, elements);
1197        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1198            (
1199                _,
1200                PnmHeader {
1201                    decoded:
1202                        HeaderRecord::Graymap(GraymapHeader {
1203                            encoding: SampleEncoding::Binary,
1204                            width: 4,
1205                            height: 4,
1206                            maxwhite: 255,
1207                        }),
1208                    encoded: _,
1209                },
1210            ) => (),
1211            _ => panic!("Decoded header is incorrect"),
1212        }
1213    }
1214
1215    #[test]
1216    fn pgm_ascii() {
1217        // The data contains two rows of the image (each line is padded to the full byte). For
1218        // comments on its format, see documentation of `impl SampleType for PbmBit`.
1219        let pbmbinary = b"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
1220        let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
1221        assert_eq!(decoder.color_type(), ColorType::L8);
1222        assert_eq!(decoder.dimensions(), (4, 4));
1223        assert_eq!(
1224            decoder.subtype(),
1225            PnmSubtype::Graymap(SampleEncoding::Ascii)
1226        );
1227        let mut image = vec![0; decoder.total_bytes() as usize];
1228        decoder.read_image(&mut image).unwrap();
1229        assert_eq!(image, (0..16).collect::<Vec<_>>());
1230        match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
1231            (
1232                _,
1233                PnmHeader {
1234                    decoded:
1235                        HeaderRecord::Graymap(GraymapHeader {
1236                            encoding: SampleEncoding::Ascii,
1237                            width: 4,
1238                            height: 4,
1239                            maxwhite: 255,
1240                        }),
1241                    encoded: _,
1242                },
1243            ) => (),
1244            _ => panic!("Decoded header is incorrect"),
1245        }
1246    }
1247
1248    #[test]
1249    fn ppm_ascii() {
1250        let ascii = b"P3 1 1 2000\n0 1000 2000";
1251        let decoder = PnmDecoder::new(&ascii[..]).unwrap();
1252        let mut image = vec![0; decoder.total_bytes() as usize];
1253        decoder.read_image(&mut image).unwrap();
1254        assert_eq!(
1255            image,
1256            [
1257                0_u16.to_ne_bytes(),
1258                (u16::MAX / 2 + 1).to_ne_bytes(),
1259                u16::MAX.to_ne_bytes()
1260            ]
1261            .into_iter()
1262            .flatten()
1263            .collect::<Vec<_>>()
1264        );
1265    }
1266
1267    #[test]
1268    fn dimension_overflow() {
1269        let pamdata = b"P7
1270# Comment line
1271MAXVAL 255
1272TUPLTYPE RGB
1273DEPTH 3
1274WIDTH 4294967295
1275HEIGHT 4294967295
1276ENDHDR
1277\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
1278
1279        assert!(PnmDecoder::new(&pamdata[..]).is_err());
1280    }
1281
1282    #[test]
1283    fn issue_1508() {
1284        let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
1285    }
1286
1287    #[test]
1288    fn issue_1616_overflow() {
1289        let data = [
1290            80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
1291        ];
1292        // Validate: we have a header. Note: we might already calculate that this will fail but
1293        // then we could not return information about the header to the caller.
1294        let decoder = PnmDecoder::new(&data[..]).unwrap();
1295        let mut image = vec![0; decoder.total_bytes() as usize];
1296        let _ = decoder.read_image(&mut image);
1297    }
1298}