Coverage Report

Created: 2026-03-10 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/gif-0.14.1/src/reader/mod.rs
Line
Count
Source
1
use alloc::borrow::Cow;
2
use alloc::vec::Vec;
3
use core::convert::{TryFrom, TryInto};
4
use core::iter::FusedIterator;
5
use core::mem;
6
use core::num::NonZeroU64;
7
use std::io;
8
use std::io::prelude::*;
9
10
use crate::common::{Block, Frame};
11
use crate::{AnyExtension, Extension, Repeat};
12
13
mod converter;
14
mod decoder;
15
16
pub use self::decoder::{
17
    Decoded, DecodingError, DecodingFormatError, FrameDataType, FrameDecoder, OutputBuffer,
18
    StreamingDecoder, Version, PLTE_CHANNELS,
19
};
20
21
pub use self::converter::ColorOutput;
22
use self::converter::PixelConverter;
23
24
#[derive(Clone, Debug)]
25
/// The maximum amount of memory the decoder is allowed to use for each frame
26
pub enum MemoryLimit {
27
    /// Enforce no memory limit.
28
    ///
29
    /// If you intend to process images from unknown origins this is a potentially dangerous
30
    /// constant to use, as your program could be vulnerable to decompression bombs. That is,
31
    /// malicious images crafted specifically to require an enormous amount of memory to process
32
    /// while having a disproportionately small file size.
33
    ///
34
    /// The risks for modern machines are a bit smaller as the size of each frame cannot
35
    /// exceed 16GiB, but this is still a significant amount of memory.
36
    Unlimited,
37
    /// Limit the amount of memory that can be used for a single frame to this many bytes.
38
    ///
39
    /// It may not be enforced precisely due to allocator overhead
40
    /// and the decoder potentially allocating small auxiliary buffers,
41
    /// but it will precisely limit the size of the output buffer for each frame.
42
    //
43
    // The `NonZero` type is used to make FFI simpler.
44
    // Due to the guaranteed niche optimization, `Unlimited` will be represented as `0`,
45
    // and the whole enum as a simple `u64`.
46
    Bytes(NonZeroU64),
47
}
48
49
impl MemoryLimit {
50
1.38M
    fn check_size(&self, size: usize) -> Result<(), DecodingError> {
51
1.38M
        match self {
52
0
            Self::Unlimited => Ok(()),
53
1.38M
            Self::Bytes(limit) => {
54
1.38M
                if size as u64 <= limit.get() {
55
1.38M
                    Ok(())
56
                } else {
57
0
                    Err(DecodingError::MemoryLimit)
58
                }
59
            }
60
        }
61
1.38M
    }
62
63
0
    fn buffer_size(&self, color: ColorOutput, width: u16, height: u16) -> Option<usize> {
64
0
        let pixels = u64::from(width) * u64::from(height);
65
66
0
        let bytes_per_pixel = match color {
67
0
            ColorOutput::Indexed => 1,
68
0
            ColorOutput::RGBA => 4,
69
        };
70
71
        // This cannot overflow because the maximum possible value is 16GiB, well within u64 range
72
0
        let total_bytes = pixels * bytes_per_pixel;
73
74
        // On 32-bit platforms the size of the output buffer may not be representable
75
0
        let usize_bytes = usize::try_from(total_bytes).ok()?;
76
77
0
        match self {
78
0
            Self::Unlimited => Some(usize_bytes),
79
0
            Self::Bytes(limit) => {
80
0
                if total_bytes > limit.get() {
81
0
                    None
82
                } else {
83
0
                    Some(usize_bytes)
84
                }
85
            }
86
        }
87
0
    }
88
89
    #[inline]
90
1.38M
    fn try_reserve(&self, vec: &mut Vec<u8>, additional: usize) -> Result<(), DecodingError> {
91
1.38M
        let len = vec
92
1.38M
            .len()
93
1.38M
            .checked_add(additional)
94
1.38M
            .ok_or(DecodingError::MemoryLimit)?;
95
1.38M
        self.check_size(len)?;
96
1.38M
        vec.try_reserve(additional)
97
1.38M
            .map_err(|_| DecodingError::OutOfMemory)?;
98
1.38M
        Ok(())
99
1.38M
    }
<gif::reader::MemoryLimit>::try_reserve
Line
Count
Source
90
1.38M
    fn try_reserve(&self, vec: &mut Vec<u8>, additional: usize) -> Result<(), DecodingError> {
91
1.38M
        let len = vec
92
1.38M
            .len()
93
1.38M
            .checked_add(additional)
94
1.38M
            .ok_or(DecodingError::MemoryLimit)?;
95
1.38M
        self.check_size(len)?;
96
1.38M
        vec.try_reserve(additional)
97
1.38M
            .map_err(|_| DecodingError::OutOfMemory)?;
98
1.38M
        Ok(())
99
1.38M
    }
Unexecuted instantiation: <gif::reader::MemoryLimit>::try_reserve
100
}
101
102
/// Options for opening a GIF decoder. [`DecodeOptions::read_info`] will start the decoder.
103
#[derive(Clone, Debug)]
104
pub struct DecodeOptions {
105
    memory_limit: MemoryLimit,
106
    color_output: ColorOutput,
107
    check_frame_consistency: bool,
108
    skip_frame_decoding: bool,
109
    check_for_end_code: bool,
110
    allow_unknown_blocks: bool,
111
}
112
113
impl Default for DecodeOptions {
114
0
    fn default() -> Self {
115
0
        Self::new()
116
0
    }
117
}
118
119
impl DecodeOptions {
120
    /// Creates a new decoder builder
121
    #[must_use]
122
    #[inline]
123
2.80k
    pub fn new() -> Self {
124
2.80k
        Self {
125
2.80k
            memory_limit: MemoryLimit::Bytes(50_000_000.try_into().unwrap()), // 50 MB
126
2.80k
            color_output: ColorOutput::Indexed,
127
2.80k
            check_frame_consistency: false,
128
2.80k
            skip_frame_decoding: false,
129
2.80k
            check_for_end_code: false,
130
2.80k
            allow_unknown_blocks: false,
131
2.80k
        }
132
2.80k
    }
<gif::reader::DecodeOptions>::new
Line
Count
Source
123
2.80k
    pub fn new() -> Self {
124
2.80k
        Self {
125
2.80k
            memory_limit: MemoryLimit::Bytes(50_000_000.try_into().unwrap()), // 50 MB
126
2.80k
            color_output: ColorOutput::Indexed,
127
2.80k
            check_frame_consistency: false,
128
2.80k
            skip_frame_decoding: false,
129
2.80k
            check_for_end_code: false,
130
2.80k
            allow_unknown_blocks: false,
131
2.80k
        }
132
2.80k
    }
Unexecuted instantiation: <gif::reader::DecodeOptions>::new
133
134
    /// Configure how color data is decoded.
135
    #[inline]
136
2.80k
    pub fn set_color_output(&mut self, color: ColorOutput) {
137
2.80k
        self.color_output = color;
138
2.80k
    }
<gif::reader::DecodeOptions>::set_color_output
Line
Count
Source
136
2.80k
    pub fn set_color_output(&mut self, color: ColorOutput) {
137
2.80k
        self.color_output = color;
138
2.80k
    }
Unexecuted instantiation: <gif::reader::DecodeOptions>::set_color_output
139
140
    /// Configure a memory limit for decoding.
141
0
    pub fn set_memory_limit(&mut self, limit: MemoryLimit) {
142
0
        self.memory_limit = limit;
143
0
    }
144
145
    /// Configure if frames must be within the screen descriptor.
146
    ///
147
    /// The default is `false`.
148
    ///
149
    /// When turned on, all frame descriptors being read must fit within the screen descriptor or
150
    /// otherwise an error is returned and the stream left in an unspecified state.
151
    ///
152
    /// When turned off, frames may be arbitrarily larger or offset in relation to the screen. Many
153
    /// other decoder libraries handle this in highly divergent ways. This moves all checks to the
154
    /// caller, for example to emulate a specific style.
155
0
    pub fn check_frame_consistency(&mut self, check: bool) {
156
0
        self.check_frame_consistency = check;
157
0
    }
158
159
    /// Configure whether to skip decoding frames.
160
    ///
161
    /// The default is false.
162
    ///
163
    /// When turned on, LZW decoding is skipped. [`Decoder::read_next_frame`] will return
164
    /// compressed LZW bytes in frame's data.
165
    /// [`Decoder::next_frame_info`] will return the metadata of the next frame as usual.
166
    /// This is useful to count frames without incurring the overhead of decoding.
167
0
    pub fn skip_frame_decoding(&mut self, skip: bool) {
168
0
        self.skip_frame_decoding = skip;
169
0
    }
170
171
    /// Configure if LZW encoded blocks must end with a marker end code.
172
    ///
173
    /// The default is `false`.
174
    ///
175
    /// When turned on, all image data blocks—which are LZW encoded—must contain a special bit
176
    /// sequence signalling the end of the data. LZW processing terminates when this code is
177
    /// encountered. The specification states that it must be the last code output by the encoder
178
    /// for an image.
179
    ///
180
    /// When turned off then image data blocks can simply end. Note that this might silently ignore
181
    /// some bits of the last or second to last byte.
182
0
    pub fn check_lzw_end_code(&mut self, check: bool) {
183
0
        self.check_for_end_code = check;
184
0
    }
185
186
    /// Configure if unknown blocks are allowed to be decoded.
187
    ///
188
    /// The default is `false`.
189
    ///
190
    /// When turned on, the decoder will allow unknown blocks to be in the
191
    /// `BlockStart` position.
192
    ///
193
    /// When turned off, decoded block starts must mark an `Image`, `Extension`,
194
    /// or `Trailer` block. Otherwise, the decoded image will return an error.
195
    /// If an unknown block error is returned from decoding, enabling this
196
    /// setting may allow for a further state of decoding on the next attempt.
197
    ///
198
    /// This option also allows unknown extension blocks. The decoder assumes the follow the same
199
    /// block layout, i.e. a sequence of zero-length terminated sub-blocks immediately follow the
200
    /// extension introducer.
201
0
    pub fn allow_unknown_blocks(&mut self, check: bool) {
202
0
        self.allow_unknown_blocks = check;
203
0
    }
204
205
    /// Reads the logical screen descriptor including the global color palette
206
    ///
207
    /// Returns a [`Decoder`]. All decoder configuration has to be done beforehand.
208
2.80k
    pub fn read_info<R: Read>(self, r: R) -> Result<Decoder<R>, DecodingError> {
209
2.80k
        Decoder::with_no_init(r, StreamingDecoder::with_options(&self), self).init()
210
2.80k
    }
<gif::reader::DecodeOptions>::read_info::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
208
2.80k
    pub fn read_info<R: Read>(self, r: R) -> Result<Decoder<R>, DecodingError> {
209
2.80k
        Decoder::with_no_init(r, StreamingDecoder::with_options(&self), self).init()
210
2.80k
    }
Unexecuted instantiation: <gif::reader::DecodeOptions>::read_info::<_>
211
}
212
213
struct ReadDecoder<R: Read> {
214
    reader: io::BufReader<R>,
215
    decoder: StreamingDecoder,
216
    at_eof: bool,
217
}
218
219
impl<R: Read> ReadDecoder<R> {
220
    #[inline(never)]
221
1.91M
    fn decode_next(
222
1.91M
        &mut self,
223
1.91M
        write_into: &mut OutputBuffer<'_>,
224
1.91M
    ) -> Result<Option<Decoded>, DecodingError> {
225
1.92M
        while !self.at_eof {
226
1.91M
            let (consumed, result) = {
227
1.92M
                let buf = self.reader.fill_buf()?;
228
1.92M
                if buf.is_empty() {
229
2.13k
                    return Err(DecodingError::UnexpectedEof);
230
1.91M
                }
231
232
1.91M
                self.decoder.update(buf, write_into)?
233
            };
234
1.91M
            self.reader.consume(consumed);
235
17.3k
            match result {
236
2.90k
                Decoded::Nothing => (),
237
5
                Decoded::BlockStart(Block::Trailer) => {
238
5
                    self.at_eof = true;
239
5
                }
240
1.91M
                result => return Ok(Some(result)),
241
            }
242
        }
243
5
        Ok(None)
244
1.91M
    }
<gif::reader::ReadDecoder<std::io::cursor::Cursor<&[u8]>>>::decode_next
Line
Count
Source
221
1.91M
    fn decode_next(
222
1.91M
        &mut self,
223
1.91M
        write_into: &mut OutputBuffer<'_>,
224
1.91M
    ) -> Result<Option<Decoded>, DecodingError> {
225
1.92M
        while !self.at_eof {
226
1.91M
            let (consumed, result) = {
227
1.92M
                let buf = self.reader.fill_buf()?;
228
1.92M
                if buf.is_empty() {
229
2.13k
                    return Err(DecodingError::UnexpectedEof);
230
1.91M
                }
231
232
1.91M
                self.decoder.update(buf, write_into)?
233
            };
234
1.91M
            self.reader.consume(consumed);
235
17.3k
            match result {
236
2.90k
                Decoded::Nothing => (),
237
5
                Decoded::BlockStart(Block::Trailer) => {
238
5
                    self.at_eof = true;
239
5
                }
240
1.91M
                result => return Ok(Some(result)),
241
            }
242
        }
243
5
        Ok(None)
244
1.91M
    }
Unexecuted instantiation: <gif::reader::ReadDecoder<_>>::decode_next
245
246
0
    fn into_inner(self) -> io::BufReader<R> {
247
0
        self.reader
248
0
    }
249
250
415k
    fn decode_next_bytes(&mut self, out: &mut OutputBuffer<'_>) -> Result<usize, DecodingError> {
251
415k
        match self.decode_next(out)? {
252
413k
            Some(Decoded::BytesDecoded(len)) => Ok(len.get()),
253
126
            Some(Decoded::DataEnd) => Ok(0),
254
0
            _ => Err(DecodingError::format("unexpected data")),
255
        }
256
415k
    }
<gif::reader::ReadDecoder<std::io::cursor::Cursor<&[u8]>>>::decode_next_bytes
Line
Count
Source
250
415k
    fn decode_next_bytes(&mut self, out: &mut OutputBuffer<'_>) -> Result<usize, DecodingError> {
251
415k
        match self.decode_next(out)? {
252
413k
            Some(Decoded::BytesDecoded(len)) => Ok(len.get()),
253
126
            Some(Decoded::DataEnd) => Ok(0),
254
0
            _ => Err(DecodingError::format("unexpected data")),
255
        }
256
415k
    }
Unexecuted instantiation: <gif::reader::ReadDecoder<_>>::decode_next_bytes
257
}
258
/// Headers for supported extensions.
259
const EXT_NAME_NETSCAPE: &[u8] = b"NETSCAPE2.0";
260
const EXT_NAME_XMP: &[u8] = b"XMP DataXMP";
261
const EXT_NAME_ICC: &[u8] = b"ICCRGBG1012";
262
263
/// State when parsing application extension
264
enum AppExtensionState {
265
    /// Waiting for app name
266
    None,
267
    Netscape,
268
    Xmp,
269
    Icc,
270
    Skip,
271
}
272
273
#[allow(dead_code)]
274
/// GIF decoder. Create [`DecodeOptions`] to get started, and call [`DecodeOptions::read_info`].
275
pub struct Decoder<R: Read> {
276
    decoder: ReadDecoder<R>,
277
    pixel_converter: PixelConverter,
278
    memory_limit: MemoryLimit,
279
    bg_color: Option<u8>,
280
    repeat: Repeat,
281
    current_frame: Frame<'static>,
282
    current_frame_data_type: FrameDataType,
283
    app_extension_state: AppExtensionState,
284
    /// XMP metadata bytes.
285
    xmp_metadata: Option<Vec<u8>>,
286
    /// ICC profile bytes.
287
    icc_profile: Option<Vec<u8>>,
288
}
289
290
impl<R> Decoder<R>
291
where
292
    R: Read,
293
{
294
    /// Create a new decoder with default options.
295
    #[inline]
296
0
    pub fn new(reader: R) -> Result<Self, DecodingError> {
297
0
        DecodeOptions::new().read_info(reader)
298
0
    }
299
300
    /// Return a builder that allows configuring limits etc.
301
    #[must_use]
302
    #[inline]
303
0
    pub fn build() -> DecodeOptions {
304
0
        DecodeOptions::new()
305
0
    }
306
307
2.80k
    fn with_no_init(reader: R, decoder: StreamingDecoder, options: DecodeOptions) -> Self {
308
2.80k
        Self {
309
2.80k
            decoder: ReadDecoder {
310
2.80k
                reader: io::BufReader::new(reader),
311
2.80k
                decoder,
312
2.80k
                at_eof: false,
313
2.80k
            },
314
2.80k
            bg_color: None,
315
2.80k
            pixel_converter: PixelConverter::new(options.color_output),
316
2.80k
            memory_limit: options.memory_limit.clone(),
317
2.80k
            repeat: Repeat::default(),
318
2.80k
            current_frame: Frame::default(),
319
2.80k
            current_frame_data_type: FrameDataType::Pixels,
320
2.80k
            app_extension_state: AppExtensionState::None,
321
2.80k
            xmp_metadata: None,
322
2.80k
            icc_profile: None,
323
2.80k
        }
324
2.80k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::with_no_init
Line
Count
Source
307
2.80k
    fn with_no_init(reader: R, decoder: StreamingDecoder, options: DecodeOptions) -> Self {
308
2.80k
        Self {
309
2.80k
            decoder: ReadDecoder {
310
2.80k
                reader: io::BufReader::new(reader),
311
2.80k
                decoder,
312
2.80k
                at_eof: false,
313
2.80k
            },
314
2.80k
            bg_color: None,
315
2.80k
            pixel_converter: PixelConverter::new(options.color_output),
316
2.80k
            memory_limit: options.memory_limit.clone(),
317
2.80k
            repeat: Repeat::default(),
318
2.80k
            current_frame: Frame::default(),
319
2.80k
            current_frame_data_type: FrameDataType::Pixels,
320
2.80k
            app_extension_state: AppExtensionState::None,
321
2.80k
            xmp_metadata: None,
322
2.80k
            icc_profile: None,
323
2.80k
        }
324
2.80k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::with_no_init
325
326
2.80k
    fn init(mut self) -> Result<Self, DecodingError> {
327
        const APP_EXTENSION: AnyExtension = AnyExtension(Extension::Application as u8);
328
        loop {
329
1.49M
            match self.decoder.decode_next(&mut OutputBuffer::None)? {
330
2.72k
                Some(Decoded::BackgroundColor(bg_color)) => {
331
2.72k
                    self.bg_color = Some(bg_color);
332
2.72k
                }
333
2.70k
                Some(Decoded::GlobalPalette(palette)) => {
334
2.70k
                    self.pixel_converter.set_global_palette(palette.into());
335
2.70k
                }
336
                Some(Decoded::SubBlock {
337
                    ext: APP_EXTENSION,
338
1.47M
                    is_last,
339
                }) => {
340
1.47M
                    self.read_application_extension(is_last)?;
341
                }
342
1.65k
                Some(Decoded::HeaderEnd) => break,
343
                Some(_) => {
344
                    // There will be extra events when parsing application extension
345
20.2k
                    continue;
346
                }
347
                None => {
348
0
                    return Err(DecodingError::format(
349
0
                        "file does not contain any image data",
350
0
                    ))
351
                }
352
            }
353
        }
354
        // If the background color is invalid, ignore it
355
1.65k
        if let Some(palette) = self.pixel_converter.global_palette() {
356
1.32k
            if self.bg_color.unwrap_or(0) as usize >= (palette.len() / PLTE_CHANNELS) {
357
836
                self.bg_color = None;
358
836
            }
359
335
        }
360
1.65k
        Ok(self)
361
2.80k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::init
Line
Count
Source
326
2.80k
    fn init(mut self) -> Result<Self, DecodingError> {
327
        const APP_EXTENSION: AnyExtension = AnyExtension(Extension::Application as u8);
328
        loop {
329
1.49M
            match self.decoder.decode_next(&mut OutputBuffer::None)? {
330
2.72k
                Some(Decoded::BackgroundColor(bg_color)) => {
331
2.72k
                    self.bg_color = Some(bg_color);
332
2.72k
                }
333
2.70k
                Some(Decoded::GlobalPalette(palette)) => {
334
2.70k
                    self.pixel_converter.set_global_palette(palette.into());
335
2.70k
                }
336
                Some(Decoded::SubBlock {
337
                    ext: APP_EXTENSION,
338
1.47M
                    is_last,
339
                }) => {
340
1.47M
                    self.read_application_extension(is_last)?;
341
                }
342
1.65k
                Some(Decoded::HeaderEnd) => break,
343
                Some(_) => {
344
                    // There will be extra events when parsing application extension
345
20.2k
                    continue;
346
                }
347
                None => {
348
0
                    return Err(DecodingError::format(
349
0
                        "file does not contain any image data",
350
0
                    ))
351
                }
352
            }
353
        }
354
        // If the background color is invalid, ignore it
355
1.65k
        if let Some(palette) = self.pixel_converter.global_palette() {
356
1.32k
            if self.bg_color.unwrap_or(0) as usize >= (palette.len() / PLTE_CHANNELS) {
357
836
                self.bg_color = None;
358
836
            }
359
335
        }
360
1.65k
        Ok(self)
361
2.80k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::init
362
363
1.47M
    fn read_application_extension(&mut self, is_last: bool) -> Result<(), DecodingError> {
364
1.47M
        let data = self.decoder.decoder.last_ext_sub_block();
365
1.47M
        match self.app_extension_state {
366
            AppExtensionState::None => {
367
                // GIF spec requires len == 11
368
14.1k
                self.app_extension_state = match data {
369
14.1k
                    EXT_NAME_NETSCAPE => AppExtensionState::Netscape,
370
                    EXT_NAME_XMP => {
371
2.01k
                        self.xmp_metadata = Some(Vec::new());
372
2.01k
                        AppExtensionState::Xmp
373
                    }
374
                    EXT_NAME_ICC => {
375
1.17k
                        self.icc_profile = Some(Vec::new());
376
1.17k
                        AppExtensionState::Icc
377
                    }
378
9.83k
                    _ => AppExtensionState::Skip,
379
                }
380
            }
381
            AppExtensionState::Netscape => {
382
1.12k
                if let [1, rest @ ..] = data {
383
911
                    if let Ok(repeat) = rest.try_into().map(u16::from_le_bytes) {
384
694
                        self.repeat = if repeat == 0 {
385
167
                            Repeat::Infinite
386
                        } else {
387
527
                            Repeat::Finite(repeat)
388
                        };
389
217
                    }
390
218
                }
391
1.12k
                self.app_extension_state = AppExtensionState::Skip;
392
            }
393
            AppExtensionState::Xmp => {
394
1.23M
                if let Some(xmp_metadata) = &mut self.xmp_metadata {
395
                    // XMP is not written as a valid "pascal-string", so we need to stitch together
396
                    // the text from our collected sublock-lengths.
397
1.23M
                    self.memory_limit
398
1.23M
                        .try_reserve(xmp_metadata, 1 + data.len())?;
399
1.23M
                    xmp_metadata.push(data.len() as u8);
400
1.23M
                    xmp_metadata.extend_from_slice(data);
401
1.23M
                    if is_last {
402
                        // XMP adds a "ramp" of 257 bytes to the end of the metadata to let the "pascal-strings"
403
                        // parser converge to the null byte. The ramp looks like "0x01, 0xff, .., 0x01, 0x00".
404
                        // For convenience and to allow consumers to not be bothered with this implementation detail,
405
                        // we cut the ramp.
406
                        const RAMP_SIZE: usize = 257;
407
1.38k
                        if xmp_metadata.len() >= RAMP_SIZE
408
753
                            && xmp_metadata.ends_with(&[0x03, 0x02, 0x01, 0x00])
409
469
                            && xmp_metadata[xmp_metadata.len() - RAMP_SIZE..]
410
469
                                .starts_with(&[0x01, 0x0ff])
411
212
                        {
412
212
                            xmp_metadata.truncate(xmp_metadata.len() - RAMP_SIZE);
413
1.17k
                        }
414
1.23M
                    }
415
0
                }
416
            }
417
            AppExtensionState::Icc => {
418
153k
                if let Some(icc) = &mut self.icc_profile {
419
153k
                    self.memory_limit.try_reserve(icc, data.len())?;
420
153k
                    icc.extend_from_slice(data);
421
0
                }
422
            }
423
67.5k
            AppExtensionState::Skip => {}
424
        };
425
1.47M
        if is_last {
426
13.4k
            self.app_extension_state = AppExtensionState::None;
427
1.45M
        }
428
1.47M
        Ok(())
429
1.47M
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::read_application_extension
Line
Count
Source
363
1.47M
    fn read_application_extension(&mut self, is_last: bool) -> Result<(), DecodingError> {
364
1.47M
        let data = self.decoder.decoder.last_ext_sub_block();
365
1.47M
        match self.app_extension_state {
366
            AppExtensionState::None => {
367
                // GIF spec requires len == 11
368
14.1k
                self.app_extension_state = match data {
369
14.1k
                    EXT_NAME_NETSCAPE => AppExtensionState::Netscape,
370
                    EXT_NAME_XMP => {
371
2.01k
                        self.xmp_metadata = Some(Vec::new());
372
2.01k
                        AppExtensionState::Xmp
373
                    }
374
                    EXT_NAME_ICC => {
375
1.17k
                        self.icc_profile = Some(Vec::new());
376
1.17k
                        AppExtensionState::Icc
377
                    }
378
9.83k
                    _ => AppExtensionState::Skip,
379
                }
380
            }
381
            AppExtensionState::Netscape => {
382
1.12k
                if let [1, rest @ ..] = data {
383
911
                    if let Ok(repeat) = rest.try_into().map(u16::from_le_bytes) {
384
694
                        self.repeat = if repeat == 0 {
385
167
                            Repeat::Infinite
386
                        } else {
387
527
                            Repeat::Finite(repeat)
388
                        };
389
217
                    }
390
218
                }
391
1.12k
                self.app_extension_state = AppExtensionState::Skip;
392
            }
393
            AppExtensionState::Xmp => {
394
1.23M
                if let Some(xmp_metadata) = &mut self.xmp_metadata {
395
                    // XMP is not written as a valid "pascal-string", so we need to stitch together
396
                    // the text from our collected sublock-lengths.
397
1.23M
                    self.memory_limit
398
1.23M
                        .try_reserve(xmp_metadata, 1 + data.len())?;
399
1.23M
                    xmp_metadata.push(data.len() as u8);
400
1.23M
                    xmp_metadata.extend_from_slice(data);
401
1.23M
                    if is_last {
402
                        // XMP adds a "ramp" of 257 bytes to the end of the metadata to let the "pascal-strings"
403
                        // parser converge to the null byte. The ramp looks like "0x01, 0xff, .., 0x01, 0x00".
404
                        // For convenience and to allow consumers to not be bothered with this implementation detail,
405
                        // we cut the ramp.
406
                        const RAMP_SIZE: usize = 257;
407
1.38k
                        if xmp_metadata.len() >= RAMP_SIZE
408
753
                            && xmp_metadata.ends_with(&[0x03, 0x02, 0x01, 0x00])
409
469
                            && xmp_metadata[xmp_metadata.len() - RAMP_SIZE..]
410
469
                                .starts_with(&[0x01, 0x0ff])
411
212
                        {
412
212
                            xmp_metadata.truncate(xmp_metadata.len() - RAMP_SIZE);
413
1.17k
                        }
414
1.23M
                    }
415
0
                }
416
            }
417
            AppExtensionState::Icc => {
418
153k
                if let Some(icc) = &mut self.icc_profile {
419
153k
                    self.memory_limit.try_reserve(icc, data.len())?;
420
153k
                    icc.extend_from_slice(data);
421
0
                }
422
            }
423
67.5k
            AppExtensionState::Skip => {}
424
        };
425
1.47M
        if is_last {
426
13.4k
            self.app_extension_state = AppExtensionState::None;
427
1.45M
        }
428
1.47M
        Ok(())
429
1.47M
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::read_application_extension
430
431
    /// Returns the next frame info
432
1.65k
    pub fn next_frame_info(&mut self) -> Result<Option<&Frame<'static>>, DecodingError> {
433
        loop {
434
3.14k
            match self.decoder.decode_next(&mut OutputBuffer::None)? {
435
1.42k
                Some(Decoded::FrameMetadata(frame_data_type)) => {
436
1.42k
                    self.current_frame = self.decoder.decoder.current_frame_mut().take();
437
1.42k
                    self.current_frame_data_type = frame_data_type;
438
1.42k
                    if self.current_frame.palette.is_none() && self.global_palette().is_none() {
439
6
                        return Err(DecodingError::format(
440
6
                            "no color table available for current frame",
441
6
                        ));
442
1.42k
                    }
443
1.42k
                    break;
444
                }
445
1.49k
                Some(_) => (),
446
5
                None => return Ok(None),
447
            }
448
        }
449
1.42k
        Ok(Some(&self.current_frame))
450
1.65k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::next_frame_info
Line
Count
Source
432
1.65k
    pub fn next_frame_info(&mut self) -> Result<Option<&Frame<'static>>, DecodingError> {
433
        loop {
434
3.14k
            match self.decoder.decode_next(&mut OutputBuffer::None)? {
435
1.42k
                Some(Decoded::FrameMetadata(frame_data_type)) => {
436
1.42k
                    self.current_frame = self.decoder.decoder.current_frame_mut().take();
437
1.42k
                    self.current_frame_data_type = frame_data_type;
438
1.42k
                    if self.current_frame.palette.is_none() && self.global_palette().is_none() {
439
6
                        return Err(DecodingError::format(
440
6
                            "no color table available for current frame",
441
6
                        ));
442
1.42k
                    }
443
1.42k
                    break;
444
                }
445
1.49k
                Some(_) => (),
446
5
                None => return Ok(None),
447
            }
448
        }
449
1.42k
        Ok(Some(&self.current_frame))
450
1.65k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::next_frame_info
451
452
    /// Query information about the frame previously advanced with [`Self::next_frame_info`].
453
    ///
454
    /// Returns `None` past the end of file.
455
0
    pub fn current_frame_info(&self) -> Option<&Frame<'static>> {
456
0
        if self.decoder.at_eof {
457
0
            None
458
        } else {
459
0
            Some(&self.current_frame)
460
        }
461
0
    }
462
463
    /// Reads the next frame from the image.
464
    ///
465
    /// Do not call `Self::next_frame_info` beforehand.
466
    /// Deinterlaces the result.
467
    ///
468
    /// You can also call `.into_iter()` on the decoder to use it as a regular iterator.
469
0
    pub fn read_next_frame(&mut self) -> Result<Option<&Frame<'static>>, DecodingError> {
470
0
        if self.next_frame_info()?.is_some() {
471
0
            match self.current_frame_data_type {
472
                FrameDataType::Pixels => {
473
0
                    self.pixel_converter.read_frame(
474
0
                        &mut self.current_frame,
475
0
                        &mut |out| self.decoder.decode_next_bytes(out),
476
0
                        &self.memory_limit,
477
0
                    )?;
478
                }
479
0
                FrameDataType::Lzw { min_code_size } => {
480
0
                    let mut vec = if matches!(self.current_frame.buffer, Cow::Owned(_)) {
481
0
                        let mut vec =
482
0
                            mem::replace(&mut self.current_frame.buffer, Cow::Borrowed(&[]))
483
0
                                .into_owned();
484
0
                        vec.clear();
485
0
                        vec
486
                    } else {
487
0
                        Vec::new()
488
                    };
489
                    // Guesstimate 2bpp
490
0
                    vec.try_reserve(
491
0
                        usize::from(self.current_frame.width)
492
0
                            * usize::from(self.current_frame.height)
493
0
                            / 4,
494
                    )
495
0
                    .map_err(|_| DecodingError::OutOfMemory)?;
496
0
                    self.copy_lzw_into_buffer(min_code_size, &mut vec)?;
497
0
                    self.current_frame.buffer = Cow::Owned(vec);
498
                }
499
            }
500
0
            Ok(Some(&self.current_frame))
501
        } else {
502
0
            Ok(None)
503
        }
504
0
    }
505
506
    /// This is private for iterator's use
507
0
    fn take_current_frame(&mut self) -> Option<Frame<'static>> {
508
0
        if self.current_frame.buffer.is_empty() {
509
0
            return None;
510
0
        }
511
0
        Some(self.current_frame.take())
512
0
    }
513
514
    /// Reads the data of the current frame into a pre-allocated buffer.
515
    ///
516
    /// `Self::next_frame_info` needs to be called beforehand.
517
    /// The length of `buf` must be at least `Self::buffer_size`.
518
    /// Deinterlaces the result.
519
1.41k
    pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> {
520
1.41k
        self.pixel_converter
521
415k
            .read_into_buffer(&self.current_frame, buf, &mut |out| {
522
415k
                self.decoder.decode_next_bytes(out)
523
415k
            })
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::read_into_buffer::{closure#0}
Line
Count
Source
521
415k
            .read_into_buffer(&self.current_frame, buf, &mut |out| {
522
415k
                self.decoder.decode_next_bytes(out)
523
415k
            })
Unexecuted instantiation: <gif::reader::Decoder<_>>::read_into_buffer::{closure#0}
524
1.41k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::read_into_buffer
Line
Count
Source
519
1.41k
    pub fn read_into_buffer(&mut self, buf: &mut [u8]) -> Result<(), DecodingError> {
520
1.41k
        self.pixel_converter
521
1.41k
            .read_into_buffer(&self.current_frame, buf, &mut |out| {
522
                self.decoder.decode_next_bytes(out)
523
            })
524
1.41k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::read_into_buffer
525
526
0
    fn copy_lzw_into_buffer(
527
0
        &mut self,
528
0
        min_code_size: u8,
529
0
        buf: &mut Vec<u8>,
530
0
    ) -> Result<(), DecodingError> {
531
        // `write_lzw_pre_encoded_frame` smuggles `min_code_size` in the first byte.
532
0
        buf.push(min_code_size);
533
        loop {
534
0
            match self.decoder.decode_next(&mut OutputBuffer::Vec(buf))? {
535
0
                Some(Decoded::LzwDataCopied(_len)) => {}
536
0
                Some(Decoded::DataEnd) => return Ok(()),
537
0
                _ => return Err(DecodingError::format("unexpected data")),
538
            }
539
        }
540
0
    }
541
542
    /// Reads data of the current frame into a pre-allocated buffer until the buffer has been
543
    /// filled completely.
544
    ///
545
    /// The buffer length must be an even number of pixels (multiple of 4 if decoding RGBA).
546
    ///
547
    /// `Self::next_frame_info` needs to be called beforehand. Returns `true` if the supplied
548
    /// buffer could be filled completely. Should not be called after `false` had been returned.
549
0
    pub fn fill_buffer(&mut self, buf: &mut [u8]) -> Result<bool, DecodingError> {
550
0
        self.pixel_converter
551
0
            .fill_buffer(&self.current_frame, buf, &mut |out| {
552
0
                self.decoder.decode_next_bytes(out)
553
0
            })
554
0
    }
555
556
    /// Output buffer size
557
0
    pub fn buffer_size(&self) -> usize {
558
0
        self.pixel_converter
559
0
            .buffer_size(&self.current_frame)
560
0
            .unwrap()
561
0
    }
562
563
    /// Line length of the current frame
564
0
    pub fn line_length(&self) -> usize {
565
0
        self.pixel_converter.line_length(&self.current_frame)
566
0
    }
567
568
    /// Returns the color palette relevant for the frame that has been decoded
569
    #[inline]
570
0
    pub fn palette(&self) -> Result<&[u8], DecodingError> {
571
0
        Ok(match self.current_frame.palette {
572
0
            Some(ref table) => table,
573
0
            None => self.global_palette().ok_or_else(|| {
574
0
                DecodingError::format("no color table available for current frame")
575
0
            })?,
576
        })
577
0
    }
578
579
    /// The global color palette
580
1.29k
    pub fn global_palette(&self) -> Option<&[u8]> {
581
1.29k
        self.pixel_converter.global_palette()
582
1.29k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::global_palette
Line
Count
Source
580
1.29k
    pub fn global_palette(&self) -> Option<&[u8]> {
581
1.29k
        self.pixel_converter.global_palette()
582
1.29k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::global_palette
583
584
    /// Width of the image
585
    #[inline]
586
9.69k
    pub fn width(&self) -> u16 {
587
9.69k
        self.decoder.decoder.width()
588
9.69k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::width
Line
Count
Source
586
9.69k
    pub fn width(&self) -> u16 {
587
9.69k
        self.decoder.decoder.width()
588
9.69k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::width
589
590
    /// Height of the image
591
    #[inline]
592
9.69k
    pub fn height(&self) -> u16 {
593
9.69k
        self.decoder.decoder.height()
594
9.69k
    }
<gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::height
Line
Count
Source
592
9.69k
    pub fn height(&self) -> u16 {
593
9.69k
        self.decoder.decoder.height()
594
9.69k
    }
Unexecuted instantiation: <gif::reader::Decoder<_>>::height
595
596
    /// XMP metadata stored in the image.
597
    #[inline]
598
    #[must_use]
599
0
    pub fn xmp_metadata(&self) -> Option<&[u8]> {
600
0
        self.xmp_metadata.as_deref()
601
0
    }
Unexecuted instantiation: <gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::xmp_metadata
Unexecuted instantiation: <gif::reader::Decoder<_>>::xmp_metadata
602
603
    /// ICC profile stored in the image.
604
    #[inline]
605
    #[must_use]
606
0
    pub fn icc_profile(&self) -> Option<&[u8]> {
607
0
        self.icc_profile.as_deref()
608
0
    }
Unexecuted instantiation: <gif::reader::Decoder<std::io::cursor::Cursor<&[u8]>>>::icc_profile
Unexecuted instantiation: <gif::reader::Decoder<_>>::icc_profile
609
610
    /// Abort decoding and recover the `io::Read` instance
611
0
    pub fn into_inner(self) -> io::BufReader<R> {
612
0
        self.decoder.into_inner()
613
0
    }
614
615
    /// Index of the background color in the global palette
616
    ///
617
    /// In practice this is not used, and the background is
618
    /// always transparent
619
0
    pub fn bg_color(&self) -> Option<usize> {
620
0
        self.bg_color.map(|v| v as usize)
621
0
    }
622
623
    /// Number of loop repetitions
624
    #[inline]
625
0
    pub fn repeat(&self) -> Repeat {
626
0
        self.repeat
627
0
    }
628
}
629
630
impl<R: Read> IntoIterator for Decoder<R> {
631
    type Item = Result<Frame<'static>, DecodingError>;
632
    type IntoIter = DecoderIter<R>;
633
634
    #[inline]
635
0
    fn into_iter(self) -> Self::IntoIter {
636
0
        DecoderIter {
637
0
            inner: self,
638
0
            ended: false,
639
0
        }
640
0
    }
641
}
642
643
/// Use `decoder.into_iter()` to iterate over the frames
644
pub struct DecoderIter<R: Read> {
645
    inner: Decoder<R>,
646
    ended: bool,
647
}
648
649
impl<R: Read> DecoderIter<R> {
650
    /// Abort decoding and recover the `io::Read` instance
651
    ///
652
    /// Use `for frame in iter.by_ref()` to be able to call this afterwards.
653
0
    pub fn into_inner(self) -> io::BufReader<R> {
654
0
        self.inner.into_inner()
655
0
    }
656
}
657
658
impl<R: Read> FusedIterator for DecoderIter<R> {}
659
660
impl<R: Read> Iterator for DecoderIter<R> {
661
    type Item = Result<Frame<'static>, DecodingError>;
662
663
0
    fn next(&mut self) -> Option<Self::Item> {
664
0
        if !self.ended {
665
0
            match self.inner.read_next_frame() {
666
0
                Ok(Some(_)) => self.inner.take_current_frame().map(Ok),
667
                Ok(None) => {
668
0
                    self.ended = true;
669
0
                    None
670
                }
671
0
                Err(err) => {
672
0
                    self.ended = true;
673
0
                    Some(Err(err))
674
                }
675
            }
676
        } else {
677
0
            None
678
        }
679
0
    }
680
}