Coverage Report

Created: 2025-07-11 07:25

/rust/registry/src/index.crates.io-6f17d22bba15001f/gif-0.13.3/src/encoder.rs
Line
Count
Source (jump to first uncovered line)
1
//! # Minimal gif encoder
2
use std::io;
3
use std::io::prelude::*;
4
use std::fmt;
5
use std::error;
6
use std::borrow::Cow;
7
8
use weezl::{BitOrder, encode::Encoder as LzwEncoder};
9
10
use crate::traits::WriteBytesExt;
11
use crate::common::{AnyExtension, Block, DisposalMethod, Extension, Frame};
12
13
/// The image has incorrect properties, making it impossible to encode as a gif.
14
#[derive(Debug)]
15
#[non_exhaustive]
16
pub enum EncodingFormatError {
17
    /// The image has too many colors.
18
    TooManyColors,
19
    /// The image has no color palette which is required.
20
    MissingColorPalette,
21
    /// LZW data is not valid for GIF. This may happen when wrong buffer is given to `write_lzw_pre_encoded_frame`
22
    InvalidMinCodeSize,
23
}
24
25
impl error::Error for EncodingFormatError {}
26
impl fmt::Display for EncodingFormatError {
27
    #[cold]
28
0
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
29
0
        match self {
30
0
            Self::TooManyColors => write!(fmt, "the image has too many colors"),
31
0
            Self::MissingColorPalette => write!(fmt, "the GIF format requires a color palette but none was given"),
32
0
            Self::InvalidMinCodeSize => write!(fmt, "LZW data is invalid"),
33
        }
34
0
    }
35
}
36
37
#[derive(Debug)]
38
/// Encoding error.
39
pub enum EncodingError {
40
    /// Returned if the to image is not encodable as a gif.
41
    Format(EncodingFormatError),
42
    /// Wraps `std::io::Error`.
43
    Io(io::Error),
44
}
45
46
impl fmt::Display for EncodingError {
47
    #[cold]
48
0
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
49
0
        match self {
50
0
            Self::Io(err) => err.fmt(fmt),
51
0
            Self::Format(err) => err.fmt(fmt),
52
        }
53
0
    }
54
}
55
56
impl error::Error for EncodingError {
57
    #[cold]
58
0
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
59
0
        match self {
60
0
            Self::Io(err) => Some(err),
61
0
            Self::Format(err) => Some(err),
62
        }
63
0
    }
64
}
65
66
impl From<io::Error> for EncodingError {
67
    #[cold]
68
0
    fn from(err: io::Error) -> Self {
69
0
        Self::Io(err)
70
0
    }
71
}
72
73
impl From<EncodingFormatError> for EncodingError {
74
    #[cold]
75
0
    fn from(err: EncodingFormatError) -> Self {
76
0
        Self::Format(err)
77
0
    }
78
}
79
80
/// Number of repetitions
81
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
82
pub enum Repeat {
83
    /// Finite number of repetitions
84
    Finite(u16),
85
    /// Infinite number of repetitions
86
    Infinite,
87
}
88
89
impl Default for Repeat {
90
1.84k
    fn default() -> Self {
91
1.84k
        Self::Finite(0)
92
1.84k
    }
93
}
94
95
/// Extension data.
96
#[non_exhaustive]
97
pub enum ExtensionData {
98
    /// Control extension. Use `ExtensionData::new_control_ext` to construct.
99
    Control {
100
        /// Flags.
101
        flags: u8,
102
        /// Frame delay.
103
        delay: u16,
104
        /// Transparent index.
105
        trns: u8,
106
    },
107
    /// Sets the number of repetitions
108
    Repetitions(Repeat),
109
}
110
111
impl ExtensionData {
112
    /// Constructor for control extension data.
113
    ///
114
    /// `delay` is given in units of 10 ms.
115
    #[must_use]
116
0
    pub fn new_control_ext(delay: u16, dispose: DisposalMethod, needs_user_input: bool, trns: Option<u8>) -> Self {
117
0
        let mut flags = 0;
118
0
        let trns = match trns {
119
0
            Some(trns) => {
120
0
                flags |= 1;
121
0
                trns
122
            },
123
0
            None => 0,
124
        };
125
0
        flags |= u8::from(needs_user_input) << 1;
126
0
        flags |= (dispose as u8) << 2;
127
0
        Self::Control { flags, delay, trns }
128
0
    }
129
}
130
131
impl<W: Write> Encoder<W> {
132
    /// Creates a new encoder.
133
    ///
134
    /// `global_palette` gives the global color palette in the format `[r, g, b, ...]`,
135
    /// if no global palette shall be used an empty slice may be supplied.
136
0
    pub fn new(w: W, width: u16, height: u16, global_palette: &[u8]) -> Result<Self, EncodingError> {
137
0
        Self {
138
0
            w: Some(w),
139
0
            global_palette: false,
140
0
            width, height,
141
0
            buffer: Vec::new(),
142
0
        }.write_global_palette(global_palette)
143
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::new
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new
144
145
    /// Write an extension block that signals a repeat behaviour.
146
0
    pub fn set_repeat(&mut self, repeat: Repeat) -> Result<(), EncodingError> {
147
0
        self.write_extension(ExtensionData::Repetitions(repeat))
148
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::set_repeat
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::set_repeat
149
150
    /// Writes the global color palette.
151
0
    fn write_global_palette(mut self, palette: &[u8]) -> Result<Self, EncodingError> {
152
0
        let mut flags = 0;
153
0
        flags |= 0b1000_0000;
154
0
        let (palette, padding, table_size) = Self::check_color_table(palette)?;
155
0
        self.global_palette = !palette.is_empty();
156
0
        // Size of global color table.
157
0
        flags |= table_size;
158
0
        // Color resolution .. FIXME. This is mostly ignored (by ImageMagick at least) but hey, we
159
0
        // should use some sensible value here or even allow configuring it?
160
0
        flags |= table_size << 4; // wtf flag
161
0
        self.write_screen_desc(flags)?;
162
0
        Self::write_color_table(self.writer()?, palette, padding)?;
163
0
        Ok(self)
164
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_global_palette
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_global_palette
165
166
    /// Writes a frame to the image.
167
    ///
168
    /// Note: This function also writes a control extension if necessary.
169
0
    pub fn write_frame(&mut self, frame: &Frame<'_>) -> Result<(), EncodingError> {
170
0
        if usize::from(frame.width).checked_mul(usize::from(frame.height)).map_or(true, |size| frame.buffer.len() < size) {
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_frame::{closure#0}
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame::{closure#0}
171
0
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "frame.buffer is too small for its width/height").into());
172
0
        }
173
0
        debug_assert!((frame.width > 0 && frame.height > 0) || frame.buffer.is_empty(), "the frame has 0 pixels, but non-empty buffer");
174
0
        self.write_frame_header(frame)?;
175
0
        self.write_image_block(&frame.buffer)
176
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_frame
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame
177
178
0
    fn write_frame_header(&mut self, frame: &Frame<'_>) -> Result<(), EncodingError> {
179
0
        self.write_extension(ExtensionData::new_control_ext(
180
0
            frame.delay,
181
0
            frame.dispose,
182
0
            frame.needs_user_input,
183
0
            frame.transparent,
184
0
        ))?;
185
0
        let mut flags = 0;
186
0
        if frame.interlaced {
187
0
            flags |= 0b0100_0000;
188
0
        }
189
0
        let palette = match frame.palette {
190
0
            Some(ref palette) => {
191
0
                flags |= 0b1000_0000;
192
0
                let (palette, padding, table_size) = Self::check_color_table(palette)?;
193
0
                flags |= table_size;
194
0
                Some((palette, padding))
195
            },
196
0
            None if self.global_palette => None,
197
0
            _ => return Err(EncodingError::from(EncodingFormatError::MissingColorPalette)),
198
        };
199
0
        let mut tmp = tmp_buf::<10>();
200
0
        tmp.write_le(Block::Image as u8)?;
201
0
        tmp.write_le(frame.left)?;
202
0
        tmp.write_le(frame.top)?;
203
0
        tmp.write_le(frame.width)?;
204
0
        tmp.write_le(frame.height)?;
205
0
        tmp.write_le(flags)?;
206
0
        let writer = self.writer()?;
207
0
        tmp.finish(&mut *writer)?;
208
0
        if let Some((palette, padding)) = palette {
209
0
            Self::write_color_table(writer, palette, padding)?;
210
0
        }
211
0
        Ok(())
212
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_frame_header
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_frame_header
213
214
0
    fn write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError> {
215
0
        self.buffer.clear();
216
0
        self.buffer.try_reserve(data.len() / 4)
217
0
            .map_err(|_| io::Error::from(io::ErrorKind::OutOfMemory))?;
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_image_block::{closure#0}
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image_block::{closure#0}
218
0
        lzw_encode(data, &mut self.buffer);
219
220
0
        let writer = self.w.as_mut().ok_or(io::Error::from(io::ErrorKind::Unsupported))?;
221
0
        Self::write_encoded_image_block(writer, &self.buffer)
222
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_image_block
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image_block
223
224
0
    fn write_encoded_image_block(writer: &mut W, data_with_min_code_size: &[u8]) -> Result<(), EncodingError> {
225
0
        let (&min_code_size, data) = data_with_min_code_size.split_first().unwrap_or((&2, &[]));
226
0
        writer.write_le(min_code_size)?;
227
228
        // Write blocks. `chunks_exact` seems to be slightly faster
229
        // than `chunks` according to both Rust docs and benchmark results.
230
0
        let mut iter = data.chunks_exact(0xFF);
231
0
        for full_block in iter.by_ref() {
232
0
            writer.write_le(0xFFu8)?;
233
0
            writer.write_all(full_block)?;
234
        }
235
0
        let last_block = iter.remainder();
236
0
        if !last_block.is_empty() {
237
0
            writer.write_le(last_block.len() as u8)?;
238
0
            writer.write_all(last_block)?;
239
0
        }
240
0
        writer.write_le(0u8).map_err(Into::into)
241
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_encoded_image_block
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_encoded_image_block
242
243
0
    fn write_color_table(writer: &mut W, table: &[u8], padding: usize) -> Result<(), EncodingError> {
244
0
        writer.write_all(table)?;
245
        // Waste some space as of gif spec
246
0
        for _ in 0..padding {
247
0
            writer.write_all(&[0, 0, 0])?;
248
        }
249
0
        Ok(())
250
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_color_table
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_color_table
251
252
    /// returns rounded palette size, number of missing colors, and table size flag
253
0
    fn check_color_table(table: &[u8]) -> Result<(&[u8], usize, u8), EncodingError> {
254
0
        let num_colors = table.len() / 3;
255
0
        if num_colors > 256 {
256
0
            return Err(EncodingError::from(EncodingFormatError::TooManyColors));
257
0
        }
258
0
        let table_size = flag_size(num_colors);
259
0
        let padding = (2 << table_size) - num_colors;
260
0
        Ok((&table[..num_colors * 3], padding, table_size))
261
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::check_color_table
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::check_color_table
262
263
    /// Writes an extension to the image.
264
    ///
265
    /// It is normally not necessary to call this method manually.
266
0
    pub fn write_extension(&mut self, extension: ExtensionData) -> Result<(), EncodingError> {
267
        use self::ExtensionData::*;
268
        // 0 finite repetitions can only be achieved
269
        // if the corresponting extension is not written
270
0
        if let Repetitions(Repeat::Finite(0)) = extension {
271
0
            return Ok(());
272
0
        }
273
0
        let writer = self.writer()?;
274
0
        writer.write_le(Block::Extension as u8)?;
275
0
        match extension {
276
0
            Control { flags, delay, trns } => {
277
0
                let mut tmp = tmp_buf::<6>();
278
0
                tmp.write_le(Extension::Control as u8)?;
279
0
                tmp.write_le(4u8)?;
280
0
                tmp.write_le(flags)?;
281
0
                tmp.write_le(delay)?;
282
0
                tmp.write_le(trns)?;
283
0
                tmp.finish(&mut *writer)?;
284
            }
285
0
            Repetitions(repeat) => {
286
0
                let mut tmp = tmp_buf::<17>();
287
0
                tmp.write_le(Extension::Application as u8)?;
288
0
                tmp.write_le(11u8)?;
289
0
                tmp.write_all(b"NETSCAPE2.0")?;
290
0
                tmp.write_le(3u8)?;
291
0
                tmp.write_le(1u8)?;
292
0
                tmp.write_le(match repeat {
293
0
                    Repeat::Finite(no) => no,
294
0
                    Repeat::Infinite => 0u16,
295
0
                })?;
296
0
                tmp.finish(&mut *writer)?;
297
            }
298
        }
299
0
        writer.write_le(0u8).map_err(Into::into)
300
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_extension
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_extension
301
302
    /// Writes a raw extension to the image.
303
    ///
304
    /// This method can be used to write an unsupported extension to the file. `func` is the extension
305
    /// identifier (e.g. `Extension::Application as u8`). `data` are the extension payload blocks. If any
306
    /// contained slice has a lenght > 255 it is automatically divided into sub-blocks.
307
0
    pub fn write_raw_extension(&mut self, func: AnyExtension, data: &[&[u8]]) -> io::Result<()> {
308
0
        let writer = self.writer()?;
309
0
        writer.write_le(Block::Extension as u8)?;
310
0
        writer.write_le(func.0)?;
311
0
        for block in data {
312
0
            for chunk in block.chunks(0xFF) {
313
0
                writer.write_le(chunk.len() as u8)?;
314
0
                writer.write_all(chunk)?;
315
            }
316
        }
317
0
        writer.write_le(0u8)
318
0
    }
319
320
    /// Writes a frame to the image, but expects `Frame.buffer` to contain LZW-encoded data
321
    /// from [`Frame::make_lzw_pre_encoded`].
322
    ///
323
    /// Note: This function also writes a control extension if necessary.
324
0
    pub fn write_lzw_pre_encoded_frame(&mut self, frame: &Frame<'_>) -> Result<(), EncodingError> {
325
        // empty data is allowed
326
0
        if let Some(&min_code_size) = frame.buffer.first() {
327
0
            if min_code_size > 11 || min_code_size < 2 {
328
0
                return Err(EncodingError::Format(EncodingFormatError::InvalidMinCodeSize));
329
0
            }
330
0
        }
331
332
0
        self.write_frame_header(frame)?;
333
0
        let writer = self.writer()?;
334
0
        Self::write_encoded_image_block(writer, &frame.buffer)
335
0
    }
336
337
    /// Writes the logical screen desriptor
338
0
    fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> {
339
0
        let mut tmp = tmp_buf::<13>();
340
0
        tmp.write_all(b"GIF89a")?;
341
0
        tmp.write_le(self.width)?;
342
0
        tmp.write_le(self.height)?;
343
0
        tmp.write_le(flags)?; // packed field
344
0
        tmp.write_le(0u8)?; // bg index
345
0
        tmp.write_le(0u8)?; // aspect ratio
346
0
        tmp.finish(self.writer()?)
347
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_screen_desc
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_screen_desc
348
349
    /// Gets a reference to the writer instance used by this encoder.
350
0
    pub fn get_ref(&self) -> &W {
351
0
        self.w.as_ref().unwrap()
352
0
    }
353
354
    /// Gets a mutable reference to the writer instance used by this encoder.
355
    ///
356
    /// It is inadvisable to directly write to the underlying writer.
357
0
    pub fn get_mut(&mut self) -> &mut W {
358
0
        self.w.as_mut().unwrap()
359
0
    }
360
361
    /// Finishes writing, and returns the `io::Write` instance used by this encoder
362
0
    pub fn into_inner(mut self) -> io::Result<W> {
363
0
        self.write_trailer()?;
364
0
        self.w.take().ok_or(io::Error::from(io::ErrorKind::Unsupported))
365
0
    }
366
367
    /// Write the final tailer.
368
0
    fn write_trailer(&mut self) -> io::Result<()> {
369
0
        self.writer()?.write_le(Block::Trailer as u8)
370
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::write_trailer
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_trailer
371
372
    #[inline]
373
0
    fn writer(&mut self) -> io::Result<&mut W> {
374
0
        self.w.as_mut().ok_or(io::Error::from(io::ErrorKind::Unsupported))
375
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_>>::writer
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::writer
376
}
377
378
/// Encodes the data into the provided buffer.
379
///
380
/// The first byte is the minimum code size, followed by LZW data.
381
0
fn lzw_encode(data: &[u8], buffer: &mut Vec<u8>) {
382
0
    let mut max_byte = 0;
383
0
    for &byte in data {
384
0
        if byte > max_byte {
385
0
            max_byte = byte;
386
0
            // code size is the same after that
387
0
            if byte > 127 {
388
0
                break;
389
0
            }
390
0
        }
391
    }
392
0
    let palette_min_len = u32::from(max_byte) + 1;
393
0
    // As per gif spec: The minimal code size has to be >= 2
394
0
    let min_code_size = palette_min_len.max(4).next_power_of_two().trailing_zeros() as u8;
395
0
    buffer.push(min_code_size);
396
0
    let mut enc = LzwEncoder::new(BitOrder::Lsb, min_code_size);
397
0
    let len = enc.into_vec(buffer).encode_all(data).consumed_out;
398
0
    buffer.truncate(len + 1);
399
0
}
400
401
impl Frame<'_> {
402
    /// Replace frame's buffer with a LZW-compressed one for use with [`Encoder::write_lzw_pre_encoded_frame`].
403
    ///
404
    /// Frames can be compressed in any order, separately from the `Encoder`, which can be used to compress frames in parallel.
405
0
    pub fn make_lzw_pre_encoded(&mut self) {
406
0
        let mut buffer = Vec::new();
407
0
        buffer.try_reserve(self.buffer.len() / 2).expect("OOM");
408
0
        lzw_encode(&self.buffer, &mut buffer);
409
0
        self.buffer = Cow::Owned(buffer);
410
0
    }
411
}
412
413
/// GIF encoder.
414
pub struct Encoder<W: Write> {
415
    w: Option<W>,
416
    global_palette: bool,
417
    width: u16,
418
    height: u16,
419
    buffer: Vec<u8>,
420
}
421
422
impl<W: Write> Drop for Encoder<W> {
423
    #[cfg(feature = "raii_no_panic")]
424
0
    fn drop(&mut self) {
425
0
        if self.w.is_some() {
426
0
            let _ = self.write_trailer();
427
0
        }
428
0
    }
Unexecuted instantiation: <gif::encoder::Encoder<_> as core::ops::drop::Drop>::drop
Unexecuted instantiation: <gif::encoder::Encoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> as core::ops::drop::Drop>::drop
429
430
    #[cfg(not(feature = "raii_no_panic"))]
431
    fn drop(&mut self) {
432
        if self.w.is_some() {
433
            self.write_trailer().unwrap();
434
        }
435
    }
436
}
437
438
// Color table size converted to flag bits
439
0
fn flag_size(size: usize) -> u8 {
440
0
    (size.clamp(2, 255).next_power_of_two().trailing_zeros() - 1) as u8
441
0
}
442
443
#[test]
444
fn test_flag_size() {
445
    fn expected(size: usize) -> u8 {
446
        match size {
447
            0  ..=2   => 0,
448
            3  ..=4   => 1,
449
            5  ..=8   => 2,
450
            9  ..=16  => 3,
451
            17 ..=32  => 4,
452
            33 ..=64  => 5,
453
            65 ..=128 => 6,
454
            129..=256 => 7,
455
            _ => 7
456
        }
457
    }
458
459
    for i in 0..300 {
460
        assert_eq!(flag_size(i), expected(i));
461
    }
462
    for i in 4..=255u8 {
463
        let expected = match flag_size(1 + i as usize) + 1 {
464
            1 => 2,
465
            n => n,
466
        };
467
        let actual = (u32::from(i) + 1).max(4).next_power_of_two().trailing_zeros() as u8;
468
        assert_eq!(actual, expected);
469
    }
470
}
471
472
struct Buf<const N: usize> {
473
    buf: [u8; N],
474
    pos: usize,
475
}
476
477
impl<const N: usize> Write for Buf<N> {
478
    #[inline(always)]
479
0
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
480
0
        let len = buf.len();
481
0
        let pos = self.pos;
482
0
        self.buf.get_mut(pos..pos + len)
483
0
            .ok_or(io::ErrorKind::WriteZero)?
484
0
            .copy_from_slice(buf);
485
0
        self.pos += len;
486
0
        Ok(len)
487
0
    }
Unexecuted instantiation: <gif::encoder::Buf<_> as std::io::Write>::write
Unexecuted instantiation: <gif::encoder::Buf<17> as std::io::Write>::write
Unexecuted instantiation: <gif::encoder::Buf<6> as std::io::Write>::write
Unexecuted instantiation: <gif::encoder::Buf<10> as std::io::Write>::write
Unexecuted instantiation: <gif::encoder::Buf<13> as std::io::Write>::write
488
489
0
    fn flush(&mut self) -> io::Result<()> { Ok(()) }
490
}
491
492
0
fn tmp_buf<const N: usize>() -> Buf<N> {
493
0
    Buf { buf: [0; N], pos: 0 }
494
0
}
Unexecuted instantiation: gif::encoder::tmp_buf::<_>
Unexecuted instantiation: gif::encoder::tmp_buf::<17>
Unexecuted instantiation: gif::encoder::tmp_buf::<6>
Unexecuted instantiation: gif::encoder::tmp_buf::<10>
Unexecuted instantiation: gif::encoder::tmp_buf::<13>
495
496
impl<const N: usize> Buf<N> {
497
    #[inline(always)]
498
0
    fn finish(&self, mut w: impl Write) -> io::Result<()> {
499
0
        debug_assert_eq!(self.pos, N);
500
0
        w.write_all(&self.buf)
501
0
    }
Unexecuted instantiation: <gif::encoder::Buf<_>>::finish::<_>
Unexecuted instantiation: <gif::encoder::Buf<17>>::finish::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: <gif::encoder::Buf<6>>::finish::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: <gif::encoder::Buf<10>>::finish::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
Unexecuted instantiation: <gif::encoder::Buf<13>>::finish::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
502
}
503
504
#[test]
505
fn error_cast() {
506
    let _: Box<dyn error::Error> = EncodingError::from(EncodingFormatError::MissingColorPalette).into();
507
}