Coverage Report

Created: 2026-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/flate2-1.1.11/src/gz/bufread.rs
Line
Count
Source
1
use crate::io;
2
use crate::io::{BufRead, Read, Write};
3
use alloc::vec::Vec;
4
use core::cmp;
5
use core::mem;
6
7
use super::{corrupt, read_into, GzBuilder, GzHeader, GzHeaderParser};
8
use crate::crc::CrcReader;
9
use crate::deflate;
10
use crate::Compression;
11
12
0
fn copy(into: &mut [u8], from: &[u8], pos: &mut usize) -> usize {
13
0
    let min = cmp::min(into.len(), from.len() - *pos);
14
0
    into[..min].copy_from_slice(&from[*pos..*pos + min]);
15
0
    *pos += min;
16
0
    min
17
0
}
Unexecuted instantiation: flate2::gz::bufread::copy
Unexecuted instantiation: flate2::gz::bufread::copy
18
19
/// A gzip streaming encoder
20
///
21
/// This structure implements a [`Read`] interface. When read from, it reads
22
/// uncompressed data from the underlying [`BufRead`] and provides the compressed data.
23
///
24
/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
25
/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
26
///
27
/// # Examples
28
///
29
/// ```
30
/// use std::io::prelude::*;
31
/// use std::io;
32
/// use flate2::Compression;
33
/// use flate2::bufread::GzEncoder;
34
/// use std::fs::File;
35
/// use std::io::BufReader;
36
///
37
/// // Opens sample file, compresses the contents and returns a Vector or error
38
/// // File wrapped in a BufReader implements BufRead
39
///
40
/// fn open_hello_world() -> io::Result<Vec<u8>> {
41
///     let f = File::open("examples/hello_world.txt")?;
42
///     let b = BufReader::new(f);
43
///     let mut gz = GzEncoder::new(b, Compression::fast());
44
///     let mut buffer = Vec::new();
45
///     gz.read_to_end(&mut buffer)?;
46
///     Ok(buffer)
47
/// }
48
/// ```
49
#[derive(Debug)]
50
pub struct GzEncoder<R> {
51
    inner: deflate::bufread::DeflateEncoder<CrcReader<R>>,
52
    header: Vec<u8>,
53
    pos: usize,
54
    eof: bool,
55
}
56
57
0
pub fn gz_encoder<R: BufRead>(header: Vec<u8>, r: R, lvl: Compression) -> GzEncoder<R> {
58
0
    let crc = CrcReader::new(r);
59
0
    GzEncoder {
60
0
        inner: deflate::bufread::DeflateEncoder::new(crc, lvl),
61
0
        header,
62
0
        pos: 0,
63
0
        eof: false,
64
0
    }
65
0
}
Unexecuted instantiation: flate2::gz::bufread::gz_encoder::<_>
Unexecuted instantiation: flate2::gz::bufread::gz_encoder::<_>
66
67
impl<R: BufRead> GzEncoder<R> {
68
    /// Creates a new encoder which will use the given compression level.
69
    ///
70
    /// The encoder is not configured specially for the emitted header. For
71
    /// header configuration, see the `GzBuilder` type.
72
    ///
73
    /// The data read from the stream `r` will be compressed and available
74
    /// through the returned reader.
75
0
    pub fn new(r: R, level: Compression) -> GzEncoder<R> {
76
0
        GzBuilder::new().buf_read(r, level)
77
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::new
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::new
78
79
0
    fn read_footer(&mut self, into: &mut [u8]) -> io::Result<usize> {
80
0
        if self.pos == 8 {
81
0
            return Ok(0);
82
0
        }
83
0
        let crc = self.inner.get_ref().crc();
84
0
        let calced_crc_bytes = crc.sum().to_le_bytes();
85
0
        let arr = [
86
0
            calced_crc_bytes[0],
87
0
            calced_crc_bytes[1],
88
0
            calced_crc_bytes[2],
89
0
            calced_crc_bytes[3],
90
0
            crc.amount() as u8,
91
0
            (crc.amount() >> 8) as u8,
92
0
            (crc.amount() >> 16) as u8,
93
0
            (crc.amount() >> 24) as u8,
94
0
        ];
95
0
        Ok(copy(into, &arr, &mut self.pos))
96
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::read_footer
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::read_footer
97
}
98
99
impl<R> GzEncoder<R> {
100
    /// Acquires a reference to the underlying reader.
101
0
    pub fn get_ref(&self) -> &R {
102
0
        self.inner.get_ref().get_ref()
103
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::get_ref
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::get_ref
104
105
    /// Acquires a mutable reference to the underlying reader.
106
    ///
107
    /// The underlying reader may be mutated as long as its unread input and
108
    /// current position are preserved for subsequent reads by this encoder.
109
    ///
110
    /// To process a new stream, wait for this encoder to reach EOF and create a
111
    /// new encoder; replacing the reader directly does not reset it.
112
0
    pub fn get_mut(&mut self) -> &mut R {
113
0
        self.inner.get_mut().get_mut()
114
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::get_mut
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::get_mut
115
116
    /// Returns the underlying stream, consuming this encoder
117
0
    pub fn into_inner(self) -> R {
118
0
        self.inner.into_inner().into_inner()
119
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::into_inner
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_>>::into_inner
120
}
121
122
#[inline]
123
0
fn finish(buf: &[u8; 8]) -> (u32, u32) {
124
0
    let crc = (buf[0] as u32)
125
0
        | ((buf[1] as u32) << 8)
126
0
        | ((buf[2] as u32) << 16)
127
0
        | ((buf[3] as u32) << 24);
128
0
    let amt = (buf[4] as u32)
129
0
        | ((buf[5] as u32) << 8)
130
0
        | ((buf[6] as u32) << 16)
131
0
        | ((buf[7] as u32) << 24);
132
0
    (crc, amt)
133
0
}
Unexecuted instantiation: flate2::gz::bufread::finish
Unexecuted instantiation: flate2::gz::bufread::finish
134
135
impl<R: BufRead> Read for GzEncoder<R> {
136
0
    fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
137
0
        if into.is_empty() {
138
0
            return Ok(0);
139
0
        }
140
0
        if self.eof {
141
0
            return self.read_footer(into);
142
0
        } else if self.pos < self.header.len() {
143
            // Report header progress before attempting a fallible input read.
144
0
            return Ok(copy(into, &self.header, &mut self.pos));
145
0
        }
146
0
        match self.inner.read(into) {
147
            Ok(0) => {
148
0
                self.eof = true;
149
0
                self.pos = 0;
150
0
                self.read_footer(into)
151
            }
152
0
            res => res,
153
        }
154
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_> as std::io::Read>::read
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_> as std::io::Read>::read
155
}
156
157
impl<R: BufRead + Write> Write for GzEncoder<R> {
158
0
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
159
0
        self.get_mut().write(buf)
160
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_> as std::io::Write>::write
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_> as std::io::Write>::write
161
162
0
    fn flush(&mut self) -> io::Result<()> {
163
0
        self.get_mut().flush()
164
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_> as std::io::Write>::flush
Unexecuted instantiation: <flate2::gz::bufread::GzEncoder<_> as std::io::Write>::flush
165
}
166
167
/// A decoder for a single member of a [gzip file].
168
///
169
/// This structure implements a [`Read`] interface. When read from, it reads
170
/// compressed data from the underlying [`BufRead`] and provides the uncompressed data.
171
///
172
/// After reading a single member of the gzip data this reader will return
173
/// Ok(0) even if there are more bytes available in the underlying reader.
174
/// If you need the following bytes, call `into_inner()` after Ok(0) to
175
/// recover the underlying reader.
176
///
177
/// To handle gzip files that may have multiple members, see [`MultiGzDecoder`]
178
/// or read more
179
/// [in the introduction](../index.html#about-multi-member-gzip-files).
180
///
181
/// [gzip file]: https://www.rfc-editor.org/rfc/rfc1952#page-5
182
/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
183
/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
184
///
185
/// # Examples
186
///
187
/// ```
188
/// use std::io::prelude::*;
189
/// use std::io;
190
/// # use flate2::Compression;
191
/// # use flate2::write::GzEncoder;
192
/// use flate2::bufread::GzDecoder;
193
///
194
/// # fn main() {
195
/// #   let mut e = GzEncoder::new(Vec::new(), Compression::default());
196
/// #   e.write_all(b"Hello World").unwrap();
197
/// #   let bytes = e.finish().unwrap();
198
/// #   println!("{}", decode_reader(bytes).unwrap());
199
/// # }
200
/// #
201
/// // Uncompresses a Gz Encoded vector of bytes and returns a string or error
202
/// // Here &[u8] implements BufRead
203
///
204
/// fn decode_reader(bytes: Vec<u8>) -> io::Result<String> {
205
///    let mut gz = GzDecoder::new(&bytes[..]);
206
///    let mut s = String::new();
207
///    gz.read_to_string(&mut s)?;
208
///    Ok(s)
209
/// }
210
/// ```
211
#[derive(Debug)]
212
pub struct GzDecoder<R> {
213
    state: GzState,
214
    reader: CrcReader<deflate::bufread::DeflateDecoder<R>>,
215
    multi: bool,
216
}
217
218
#[derive(Debug)]
219
enum GzState {
220
    Header(GzHeaderParser),
221
    Body(GzHeader),
222
    Finished(GzHeader, usize, [u8; 8]),
223
    Err(io::Error),
224
    End(Option<GzHeader>),
225
}
226
227
0
pub fn reset_decoder_data<R>(decoder: &mut GzDecoder<R>) {
228
0
    decoder.state = GzState::Header(GzHeaderParser::new());
229
0
    decoder.reader.reset(); // reset CrcReader
230
0
    decoder.reader.get_mut().reset_data(); // reset DeflateDecoder
231
0
}
Unexecuted instantiation: flate2::gz::bufread::reset_decoder_data::<_>
Unexecuted instantiation: flate2::gz::bufread::reset_decoder_data::<_>
232
233
impl<R: BufRead> GzDecoder<R> {
234
    /// Creates a new decoder from the given reader, immediately parsing the
235
    /// gzip header.
236
0
    pub fn new(mut r: R) -> GzDecoder<R> {
237
0
        let mut header_parser = GzHeaderParser::new();
238
239
0
        let state = match header_parser.parse(&mut r) {
240
0
            Ok(_) => GzState::Body(GzHeader::from(header_parser)),
241
0
            Err(ref err) if io::ErrorKind::WouldBlock == err.kind() => {
242
0
                GzState::Header(header_parser)
243
            }
244
0
            Err(err) => GzState::Err(err),
245
        };
246
247
0
        GzDecoder {
248
0
            state,
249
0
            reader: CrcReader::new(deflate::bufread::DeflateDecoder::new(r)),
250
0
            multi: false,
251
0
        }
252
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::new
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::new
253
254
0
    fn multi(mut self, flag: bool) -> GzDecoder<R> {
255
0
        self.multi = flag;
256
0
        self
257
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::multi
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::multi
258
}
259
260
impl<R> GzDecoder<R> {
261
    /// Returns the header associated with this stream, if it was valid
262
0
    pub fn header(&self) -> Option<&GzHeader> {
263
0
        match &self.state {
264
0
            GzState::Body(header) | GzState::Finished(header, _, _) => Some(header),
265
0
            GzState::End(header) => header.as_ref(),
266
0
            _ => None,
267
        }
268
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::header
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::header
269
270
    /// Acquires a reference to the underlying reader.
271
0
    pub fn get_ref(&self) -> &R {
272
0
        self.reader.get_ref().get_ref()
273
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::get_ref
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::get_ref
274
275
    /// Acquires a mutable reference to the underlying stream.
276
    ///
277
    /// The underlying reader may be mutated as long as its unread input and
278
    /// current position are preserved for subsequent reads by this decoder.
279
    ///
280
    /// To process a new stream, wait for this decoder to reach EOF and use
281
    /// [`reset`](Self::reset); replacing the reader directly does not reset it.
282
0
    pub fn get_mut(&mut self) -> &mut R {
283
0
        self.reader.get_mut().get_mut()
284
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::get_mut
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::get_mut
285
286
    /// Consumes this decoder, returning the underlying reader.
287
0
    pub fn into_inner(self) -> R {
288
0
        self.reader.into_inner().into_inner()
289
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::into_inner
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::into_inner
290
291
    /// Resets the state of this decoder entirely, swapping out the input
292
    /// stream for another.
293
    ///
294
    /// This will reset the internal state of this decoder and replace the
295
    /// input stream with the one provided, returning the previous input
296
    /// stream. Future data read from this decoder will be the decompressed
297
    /// version of `r`'s data.
298
0
    pub fn reset(&mut self, r: R) -> R {
299
0
        reset_decoder_data(self);
300
0
        self.reader.get_mut().reset(r)
301
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::reset
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_>>::reset
302
}
303
304
impl<R: BufRead> Read for GzDecoder<R> {
305
0
    fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
306
        loop {
307
0
            match &mut self.state {
308
0
                GzState::Header(parser) => {
309
0
                    parser.parse(self.reader.get_mut().get_mut())?;
310
0
                    self.state = GzState::Body(GzHeader::from(mem::take(parser)));
311
                }
312
0
                GzState::Body(header) => {
313
0
                    if into.is_empty() {
314
0
                        return Ok(0);
315
0
                    }
316
0
                    match self.reader.read(into)? {
317
0
                        0 => {
318
0
                            self.state = GzState::Finished(mem::take(header), 0, [0; 8]);
319
0
                        }
320
0
                        n => {
321
0
                            return Ok(n);
322
                        }
323
                    }
324
                }
325
0
                GzState::Finished(header, pos, buf) => {
326
0
                    if *pos < buf.len() {
327
0
                        *pos += read_into(self.reader.get_mut().get_mut(), &mut buf[*pos..])?;
328
                    } else {
329
0
                        let (crc, amt) = finish(buf);
330
331
0
                        if crc != self.reader.crc().sum() || amt != self.reader.crc().amount() {
332
0
                            self.state = GzState::End(Some(mem::take(header)));
333
0
                            return Err(corrupt());
334
0
                        } else if self.multi {
335
0
                            let is_eof = self
336
0
                                .reader
337
0
                                .get_mut()
338
0
                                .get_mut()
339
0
                                .fill_buf()
340
0
                                .map(|buf| buf.is_empty())?;
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Read>::read::{closure#0}
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Read>::read::{closure#0}
341
342
0
                            if is_eof {
343
0
                                self.state = GzState::End(Some(mem::take(header)));
344
0
                            } else {
345
0
                                self.reader.reset();
346
0
                                self.reader.get_mut().reset_data();
347
0
                                self.state = GzState::Header(GzHeaderParser::new())
348
                            }
349
0
                        } else {
350
0
                            self.state = GzState::End(Some(mem::take(header)));
351
0
                        }
352
                    }
353
                }
354
0
                GzState::Err(err) => {
355
0
                    let result = Err(mem::replace(err, io::ErrorKind::Other.into()));
356
0
                    self.state = GzState::End(None);
357
0
                    return result;
358
                }
359
0
                GzState::End(_) => return Ok(0),
360
            }
361
        }
362
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Read>::read
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Read>::read
363
}
364
365
impl<R: BufRead + Write> Write for GzDecoder<R> {
366
0
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
367
0
        self.get_mut().write(buf)
368
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Write>::write
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Write>::write
369
370
0
    fn flush(&mut self) -> io::Result<()> {
371
0
        self.get_mut().flush()
372
0
    }
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Write>::flush
Unexecuted instantiation: <flate2::gz::bufread::GzDecoder<_> as std::io::Write>::flush
373
}
374
375
/// A gzip streaming decoder that decodes a [gzip file] that may have multiple members.
376
///
377
/// This structure implements a [`Read`] interface. When read from, it reads
378
/// compressed data from the underlying [`BufRead`] and provides the uncompressed data.
379
///
380
/// A gzip file consists of a series of *members* concatenated one after another.
381
/// MultiGzDecoder decodes all members from the data and only returns Ok(0) when the
382
/// underlying reader does. For a file, this reads to the end of the file.
383
///
384
/// To handle members separately, see [GzDecoder] or read more
385
/// [in the introduction](../index.html#about-multi-member-gzip-files).
386
///
387
/// [gzip file]: https://www.rfc-editor.org/rfc/rfc1952#page-5
388
/// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
389
/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
390
///
391
/// # Examples
392
///
393
/// ```
394
/// use std::io::prelude::*;
395
/// use std::io;
396
/// # use flate2::Compression;
397
/// # use flate2::write::GzEncoder;
398
/// use flate2::bufread::MultiGzDecoder;
399
///
400
/// # fn main() {
401
/// #   let mut e = GzEncoder::new(Vec::new(), Compression::default());
402
/// #   e.write_all(b"Hello World").unwrap();
403
/// #   let bytes = e.finish().unwrap();
404
/// #   println!("{}", decode_reader(bytes).unwrap());
405
/// # }
406
/// #
407
/// // Uncompresses a Gz Encoded vector of bytes and returns a string or error
408
/// // Here &[u8] implements BufRead
409
///
410
/// fn decode_reader(bytes: Vec<u8>) -> io::Result<String> {
411
///    let mut gz = MultiGzDecoder::new(&bytes[..]);
412
///    let mut s = String::new();
413
///    gz.read_to_string(&mut s)?;
414
///    Ok(s)
415
/// }
416
/// ```
417
#[derive(Debug)]
418
pub struct MultiGzDecoder<R>(GzDecoder<R>);
419
420
impl<R: BufRead> MultiGzDecoder<R> {
421
    /// Creates a new decoder from the given reader, immediately parsing the
422
    /// (first) gzip header. If the gzip stream contains multiple members all will
423
    /// be decoded.
424
0
    pub fn new(r: R) -> MultiGzDecoder<R> {
425
0
        MultiGzDecoder(GzDecoder::new(r).multi(true))
426
0
    }
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::new
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::new
427
}
428
429
impl<R> MultiGzDecoder<R> {
430
    /// Returns the current header associated with this stream, if it's valid
431
0
    pub fn header(&self) -> Option<&GzHeader> {
432
0
        self.0.header()
433
0
    }
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::header
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::header
434
435
    /// Acquires a reference to the underlying reader.
436
0
    pub fn get_ref(&self) -> &R {
437
0
        self.0.get_ref()
438
0
    }
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::get_ref
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::get_ref
439
440
    /// Acquires a mutable reference to the underlying stream.
441
    ///
442
    /// The underlying reader may be mutated as long as its unread input and
443
    /// current position are preserved for subsequent reads by this decoder.
444
    ///
445
    /// To process a new stream, wait for this decoder to reach EOF and create a
446
    /// new decoder; replacing the reader directly does not reset it.
447
0
    pub fn get_mut(&mut self) -> &mut R {
448
0
        self.0.get_mut()
449
0
    }
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::get_mut
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::get_mut
450
451
    /// Consumes this decoder, returning the underlying reader.
452
0
    pub fn into_inner(self) -> R {
453
0
        self.0.into_inner()
454
0
    }
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::into_inner
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_>>::into_inner
455
}
456
457
impl<R: BufRead> Read for MultiGzDecoder<R> {
458
0
    fn read(&mut self, into: &mut [u8]) -> io::Result<usize> {
459
0
        self.0.read(into)
460
0
    }
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_> as std::io::Read>::read
Unexecuted instantiation: <flate2::gz::bufread::MultiGzDecoder<_> as std::io::Read>::read
461
}
462
463
#[cfg(test)]
464
mod test {
465
    use crate::bufread::GzDecoder;
466
    use crate::gz::write;
467
    use crate::io::{Read, Write};
468
    use crate::Compression;
469
    use alloc::vec::Vec;
470
471
    // GzDecoder consumes one gzip member and then returns 0 for subsequent reads, allowing any
472
    // additional data to be consumed by the caller.
473
    #[test]
474
    fn decode_extra_data() {
475
        let expected = "Hello World";
476
477
        let compressed = {
478
            let mut e = write::GzEncoder::new(Vec::new(), Compression::default());
479
            e.write_all(expected.as_ref()).unwrap();
480
            let mut b = e.finish().unwrap();
481
            b.push(b'x');
482
            b
483
        };
484
485
        let mut output = Vec::new();
486
        let mut decoder = GzDecoder::new(compressed.as_slice());
487
        let decoded_bytes = decoder.read_to_end(&mut output).unwrap();
488
        assert_eq!(decoded_bytes, output.len());
489
        let actual = core::str::from_utf8(&output).expect("String parsing error");
490
        assert_eq!(
491
            actual, expected,
492
            "after decompression we obtain the original input"
493
        );
494
495
        output.clear();
496
        assert_eq!(
497
            decoder.read(&mut output).unwrap(),
498
            0,
499
            "subsequent read of decoder returns 0, but inner reader can return additional data"
500
        );
501
        let mut reader = decoder.into_inner();
502
        assert_eq!(
503
            reader.read_to_end(&mut output).unwrap(),
504
            1,
505
            "extra data is accessible in underlying buf-read"
506
        );
507
        assert_eq!(output, b"x");
508
    }
509
510
    fn compress_data(data: &[u8]) -> Vec<u8> {
511
        use crate::write::GzEncoder;
512
        use crate::Compression;
513
514
        let mut e = GzEncoder::new(Vec::new(), Compression::default());
515
        e.write_all(data).unwrap();
516
        e.finish().unwrap()
517
    }
518
519
    #[test]
520
    fn decode_with_reset() {
521
        let data1 = b"Hello World";
522
        let data2 = b"Goodbye World";
523
524
        let compressed1 = compress_data(data1);
525
        let compressed2 = compress_data(data2);
526
527
        let mut output = Vec::new();
528
        let mut decoder = GzDecoder::new(compressed1.as_slice());
529
        decoder.read_to_end(&mut output).unwrap();
530
        assert_eq!(output, data1);
531
532
        output.clear();
533
        decoder.reset(compressed2.as_slice());
534
        decoder.read_to_end(&mut output).unwrap();
535
        assert_eq!(output, data2);
536
    }
537
538
    #[test]
539
    fn decode_with_reset_after_corruption() {
540
        let valid_data = b"Hello World";
541
        let valid_compressed = compress_data(valid_data);
542
543
        // Create a corrupted payload (valid gzip header but corrupted body)
544
        let mut corrupted = valid_compressed.clone();
545
        assert!(corrupted.len() >= 14);
546
        corrupted[12] ^= 0xFF;
547
        corrupted[13] ^= 0xFF;
548
549
        // Try to decode corrupted data
550
        let mut decoder = GzDecoder::new(corrupted.as_slice());
551
        let mut output = Vec::new();
552
        let _ = decoder.read_to_end(&mut output).unwrap_err();
553
554
        // Reset with valid payload and decode
555
        decoder.reset(valid_compressed.as_slice());
556
        output.clear();
557
        decoder.read_to_end(&mut output).unwrap();
558
        assert_eq!(output, valid_data);
559
    }
560
}