]>
Commit | Line | Data |
---|---|---|
ea8adc8c | 1 | use std::io; |
60c5eb7d XL |
2 | use std::io::prelude::*; |
3 | ||
ea8adc8c | 4 | use super::bufread; |
60c5eb7d XL |
5 | use super::{GzBuilder, GzHeader}; |
6 | use crate::bufreader::BufReader; | |
7 | use crate::Compression; | |
ea8adc8c XL |
8 | |
9 | /// A gzip streaming encoder | |
10 | /// | |
11 | /// This structure exposes a [`Read`] interface that will read uncompressed data | |
12 | /// from the underlying reader and expose the compressed version as a [`Read`] | |
13 | /// interface. | |
14 | /// | |
15 | /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html | |
16 | /// | |
17 | /// # Examples | |
18 | /// | |
19 | /// ``` | |
20 | /// use std::io::prelude::*; | |
21 | /// use std::io; | |
22 | /// use flate2::Compression; | |
23 | /// use flate2::read::GzEncoder; | |
24 | /// | |
25 | /// // Return a vector containing the GZ compressed version of hello world | |
26 | /// | |
27 | /// fn gzencode_hello_world() -> io::Result<Vec<u8>> { | |
781aab86 | 28 | /// let mut ret_vec = Vec::new(); |
ea8adc8c | 29 | /// let bytestring = b"hello world"; |
ff7c6d11 | 30 | /// let mut gz = GzEncoder::new(&bytestring[..], Compression::fast()); |
781aab86 FG |
31 | /// gz.read_to_end(&mut ret_vec)?; |
32 | /// Ok(ret_vec) | |
ea8adc8c XL |
33 | /// } |
34 | /// ``` | |
35 | #[derive(Debug)] | |
36 | pub struct GzEncoder<R> { | |
37 | inner: bufread::GzEncoder<BufReader<R>>, | |
38 | } | |
39 | ||
b7449926 | 40 | pub fn gz_encoder<R: Read>(inner: bufread::GzEncoder<BufReader<R>>) -> GzEncoder<R> { |
04454e1e | 41 | GzEncoder { inner } |
ea8adc8c XL |
42 | } |
43 | ||
44 | impl<R: Read> GzEncoder<R> { | |
45 | /// Creates a new encoder which will use the given compression level. | |
46 | /// | |
47 | /// The encoder is not configured specially for the emitted header. For | |
ff7c6d11 | 48 | /// header configuration, see the `GzBuilder` type. |
ea8adc8c XL |
49 | /// |
50 | /// The data read from the stream `r` will be compressed and available | |
51 | /// through the returned reader. | |
52 | pub fn new(r: R, level: Compression) -> GzEncoder<R> { | |
ff7c6d11 | 53 | GzBuilder::new().read(r, level) |
ea8adc8c XL |
54 | } |
55 | } | |
56 | ||
57 | impl<R> GzEncoder<R> { | |
58 | /// Acquires a reference to the underlying reader. | |
59 | pub fn get_ref(&self) -> &R { | |
60 | self.inner.get_ref().get_ref() | |
61 | } | |
62 | ||
63 | /// Acquires a mutable reference to the underlying reader. | |
64 | /// | |
65 | /// Note that mutation of the reader may result in surprising results if | |
66 | /// this encoder is continued to be used. | |
67 | pub fn get_mut(&mut self) -> &mut R { | |
68 | self.inner.get_mut().get_mut() | |
69 | } | |
70 | ||
71 | /// Returns the underlying stream, consuming this encoder | |
72 | pub fn into_inner(self) -> R { | |
73 | self.inner.into_inner().into_inner() | |
74 | } | |
75 | } | |
76 | ||
77 | impl<R: Read> Read for GzEncoder<R> { | |
78 | fn read(&mut self, into: &mut [u8]) -> io::Result<usize> { | |
79 | self.inner.read(into) | |
80 | } | |
81 | } | |
82 | ||
83 | impl<R: Read + Write> Write for GzEncoder<R> { | |
84 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
85 | self.get_mut().write(buf) | |
86 | } | |
87 | ||
88 | fn flush(&mut self) -> io::Result<()> { | |
89 | self.get_mut().flush() | |
90 | } | |
91 | } | |
92 | ||
781aab86 | 93 | /// A decoder for a single member of a [gzip file]. |
ea8adc8c XL |
94 | /// |
95 | /// This structure exposes a [`Read`] interface that will consume compressed | |
96 | /// data from the underlying reader and emit uncompressed data. | |
97 | /// | |
781aab86 FG |
98 | /// After reading a single member of the gzip data this reader will return |
99 | /// Ok(0) even if there are more bytes available in the underlying reader. | |
100 | /// `GzDecoder` may have read additional bytes past the end of the gzip data. | |
101 | /// If you need the following bytes, wrap the `Reader` in a `std::io::BufReader` | |
102 | /// and use `bufread::GzDecoder` instead. | |
103 | /// | |
104 | /// To handle gzip files that may have multiple members, see [`MultiGzDecoder`] | |
105 | /// or read more | |
106 | /// [in the introduction](../index.html#about-multi-member-gzip-files). | |
107 | /// | |
108 | /// [gzip file]: https://www.rfc-editor.org/rfc/rfc1952#page-5 | |
ea8adc8c XL |
109 | /// |
110 | /// # Examples | |
111 | /// | |
112 | /// ``` | |
ea8adc8c XL |
113 | /// use std::io::prelude::*; |
114 | /// use std::io; | |
115 | /// # use flate2::Compression; | |
116 | /// # use flate2::write::GzEncoder; | |
117 | /// use flate2::read::GzDecoder; | |
118 | /// | |
119 | /// # fn main() { | |
ff7c6d11 | 120 | /// # let mut e = GzEncoder::new(Vec::new(), Compression::default()); |
b7449926 | 121 | /// # e.write_all(b"Hello World").unwrap(); |
ea8adc8c XL |
122 | /// # let bytes = e.finish().unwrap(); |
123 | /// # println!("{}", decode_reader(bytes).unwrap()); | |
124 | /// # } | |
125 | /// # | |
126 | /// // Uncompresses a Gz Encoded vector of bytes and returns a string or error | |
127 | /// // Here &[u8] implements Read | |
128 | /// | |
129 | /// fn decode_reader(bytes: Vec<u8>) -> io::Result<String> { | |
ff7c6d11 | 130 | /// let mut gz = GzDecoder::new(&bytes[..]); |
ea8adc8c XL |
131 | /// let mut s = String::new(); |
132 | /// gz.read_to_string(&mut s)?; | |
133 | /// Ok(s) | |
134 | /// } | |
135 | /// ``` | |
136 | #[derive(Debug)] | |
137 | pub struct GzDecoder<R> { | |
138 | inner: bufread::GzDecoder<BufReader<R>>, | |
139 | } | |
140 | ||
141 | impl<R: Read> GzDecoder<R> { | |
142 | /// Creates a new decoder from the given reader, immediately parsing the | |
143 | /// gzip header. | |
ff7c6d11 XL |
144 | pub fn new(r: R) -> GzDecoder<R> { |
145 | GzDecoder { | |
146 | inner: bufread::GzDecoder::new(BufReader::new(r)), | |
147 | } | |
ea8adc8c XL |
148 | } |
149 | } | |
150 | ||
151 | impl<R> GzDecoder<R> { | |
ff7c6d11 XL |
152 | /// Returns the header associated with this stream, if it was valid. |
153 | pub fn header(&self) -> Option<&GzHeader> { | |
ea8adc8c XL |
154 | self.inner.header() |
155 | } | |
156 | ||
157 | /// Acquires a reference to the underlying reader. | |
781aab86 FG |
158 | /// |
159 | /// Note that the decoder may have read past the end of the gzip data. | |
160 | /// To prevent this use [`bufread::GzDecoder`] instead. | |
ea8adc8c XL |
161 | pub fn get_ref(&self) -> &R { |
162 | self.inner.get_ref().get_ref() | |
163 | } | |
164 | ||
165 | /// Acquires a mutable reference to the underlying stream. | |
166 | /// | |
167 | /// Note that mutation of the stream may result in surprising results if | |
781aab86 FG |
168 | /// this decoder continues to be used. |
169 | /// | |
170 | /// Note that the decoder may have read past the end of the gzip data. | |
171 | /// To prevent this use [`bufread::GzDecoder`] instead. | |
ea8adc8c XL |
172 | pub fn get_mut(&mut self) -> &mut R { |
173 | self.inner.get_mut().get_mut() | |
174 | } | |
175 | ||
176 | /// Consumes this decoder, returning the underlying reader. | |
781aab86 FG |
177 | /// |
178 | /// Note that the decoder may have read past the end of the gzip data. | |
179 | /// Subsequent reads will skip those bytes. To prevent this use | |
180 | /// [`bufread::GzDecoder`] instead. | |
ea8adc8c XL |
181 | pub fn into_inner(self) -> R { |
182 | self.inner.into_inner().into_inner() | |
183 | } | |
184 | } | |
185 | ||
186 | impl<R: Read> Read for GzDecoder<R> { | |
187 | fn read(&mut self, into: &mut [u8]) -> io::Result<usize> { | |
188 | self.inner.read(into) | |
189 | } | |
190 | } | |
191 | ||
192 | impl<R: Read + Write> Write for GzDecoder<R> { | |
193 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
194 | self.get_mut().write(buf) | |
195 | } | |
196 | ||
197 | fn flush(&mut self) -> io::Result<()> { | |
198 | self.get_mut().flush() | |
199 | } | |
200 | } | |
201 | ||
781aab86 FG |
202 | /// A gzip streaming decoder that decodes a [gzip file] that may have multiple members. |
203 | /// | |
204 | /// This structure exposes a [`Read`] interface that will consume compressed | |
205 | /// data from the underlying reader and emit uncompressed data. | |
ea8adc8c | 206 | /// |
781aab86 FG |
207 | /// A gzip file consists of a series of *members* concatenated one after another. |
208 | /// MultiGzDecoder decodes all members of a file and returns Ok(0) once the | |
209 | /// underlying reader does. | |
ea8adc8c | 210 | /// |
781aab86 FG |
211 | /// To handle members seperately, see [GzDecoder] or read more |
212 | /// [in the introduction](../index.html#about-multi-member-gzip-files). | |
ea8adc8c | 213 | /// |
781aab86 | 214 | /// [gzip file]: https://www.rfc-editor.org/rfc/rfc1952#page-5 |
ea8adc8c XL |
215 | /// |
216 | /// # Examples | |
217 | /// | |
218 | /// ``` | |
219 | /// use std::io::prelude::*; | |
220 | /// use std::io; | |
221 | /// # use flate2::Compression; | |
222 | /// # use flate2::write::GzEncoder; | |
223 | /// use flate2::read::MultiGzDecoder; | |
224 | /// | |
225 | /// # fn main() { | |
ff7c6d11 | 226 | /// # let mut e = GzEncoder::new(Vec::new(), Compression::default()); |
b7449926 | 227 | /// # e.write_all(b"Hello World").unwrap(); |
ea8adc8c XL |
228 | /// # let bytes = e.finish().unwrap(); |
229 | /// # println!("{}", decode_reader(bytes).unwrap()); | |
230 | /// # } | |
231 | /// # | |
232 | /// // Uncompresses a Gz Encoded vector of bytes and returns a string or error | |
233 | /// // Here &[u8] implements Read | |
234 | /// | |
235 | /// fn decode_reader(bytes: Vec<u8>) -> io::Result<String> { | |
ff7c6d11 | 236 | /// let mut gz = MultiGzDecoder::new(&bytes[..]); |
ea8adc8c XL |
237 | /// let mut s = String::new(); |
238 | /// gz.read_to_string(&mut s)?; | |
239 | /// Ok(s) | |
240 | /// } | |
241 | /// ``` | |
242 | #[derive(Debug)] | |
243 | pub struct MultiGzDecoder<R> { | |
244 | inner: bufread::MultiGzDecoder<BufReader<R>>, | |
245 | } | |
246 | ||
247 | impl<R: Read> MultiGzDecoder<R> { | |
248 | /// Creates a new decoder from the given reader, immediately parsing the | |
249 | /// (first) gzip header. If the gzip stream contains multiple members all will | |
250 | /// be decoded. | |
ff7c6d11 XL |
251 | pub fn new(r: R) -> MultiGzDecoder<R> { |
252 | MultiGzDecoder { | |
253 | inner: bufread::MultiGzDecoder::new(BufReader::new(r)), | |
254 | } | |
ea8adc8c XL |
255 | } |
256 | } | |
257 | ||
258 | impl<R> MultiGzDecoder<R> { | |
ff7c6d11 XL |
259 | /// Returns the current header associated with this stream, if it's valid. |
260 | pub fn header(&self) -> Option<&GzHeader> { | |
ea8adc8c XL |
261 | self.inner.header() |
262 | } | |
263 | ||
264 | /// Acquires a reference to the underlying reader. | |
265 | pub fn get_ref(&self) -> &R { | |
266 | self.inner.get_ref().get_ref() | |
267 | } | |
268 | ||
269 | /// Acquires a mutable reference to the underlying stream. | |
270 | /// | |
271 | /// Note that mutation of the stream may result in surprising results if | |
fe692bf9 | 272 | /// this decoder is continued to be used. |
ea8adc8c XL |
273 | pub fn get_mut(&mut self) -> &mut R { |
274 | self.inner.get_mut().get_mut() | |
275 | } | |
276 | ||
277 | /// Consumes this decoder, returning the underlying reader. | |
278 | pub fn into_inner(self) -> R { | |
279 | self.inner.into_inner().into_inner() | |
280 | } | |
281 | } | |
282 | ||
283 | impl<R: Read> Read for MultiGzDecoder<R> { | |
284 | fn read(&mut self, into: &mut [u8]) -> io::Result<usize> { | |
285 | self.inner.read(into) | |
286 | } | |
287 | } | |
288 | ||
289 | impl<R: Read + Write> Write for MultiGzDecoder<R> { | |
290 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
291 | self.get_mut().write(buf) | |
292 | } | |
293 | ||
294 | fn flush(&mut self) -> io::Result<()> { | |
295 | self.get_mut().flush() | |
296 | } | |
297 | } | |
781aab86 FG |
298 | |
299 | #[cfg(test)] | |
300 | mod tests { | |
301 | use std::io::{Cursor, ErrorKind, Read, Result, Write}; | |
302 | ||
303 | use super::GzDecoder; | |
304 | ||
305 | //a cursor turning EOF into blocking errors | |
306 | #[derive(Debug)] | |
307 | pub struct BlockingCursor { | |
308 | pub cursor: Cursor<Vec<u8>>, | |
309 | } | |
310 | ||
311 | impl BlockingCursor { | |
312 | pub fn new() -> BlockingCursor { | |
313 | BlockingCursor { | |
314 | cursor: Cursor::new(Vec::new()), | |
315 | } | |
316 | } | |
317 | ||
318 | pub fn set_position(&mut self, pos: u64) { | |
319 | return self.cursor.set_position(pos); | |
320 | } | |
321 | } | |
322 | ||
323 | impl Write for BlockingCursor { | |
324 | fn write(&mut self, buf: &[u8]) -> Result<usize> { | |
325 | return self.cursor.write(buf); | |
326 | } | |
327 | fn flush(&mut self) -> Result<()> { | |
328 | return self.cursor.flush(); | |
329 | } | |
330 | } | |
331 | ||
332 | impl Read for BlockingCursor { | |
333 | fn read(&mut self, buf: &mut [u8]) -> Result<usize> { | |
334 | //use the cursor, except it turns eof into blocking error | |
335 | let r = self.cursor.read(buf); | |
336 | match r { | |
337 | Err(ref err) => { | |
338 | if err.kind() == ErrorKind::UnexpectedEof { | |
339 | return Err(ErrorKind::WouldBlock.into()); | |
340 | } | |
341 | } | |
342 | Ok(0) => { | |
343 | //regular EOF turned into blocking error | |
344 | return Err(ErrorKind::WouldBlock.into()); | |
345 | } | |
346 | Ok(_n) => {} | |
347 | } | |
348 | return r; | |
349 | } | |
350 | } | |
351 | ||
352 | #[test] | |
353 | fn blocked_partial_header_read() { | |
354 | // this is a reader which receives data afterwards | |
355 | let mut r = BlockingCursor::new(); | |
356 | let data = vec![1, 2, 3]; | |
357 | ||
358 | match r.write_all(&data) { | |
359 | Ok(()) => {} | |
360 | _ => { | |
361 | panic!("Unexpected result for write_all"); | |
362 | } | |
363 | } | |
364 | r.set_position(0); | |
365 | ||
366 | // this is unused except for the buffering | |
367 | let mut decoder = GzDecoder::new(r); | |
368 | let mut out = Vec::with_capacity(7); | |
369 | match decoder.read(&mut out) { | |
370 | Err(e) => { | |
371 | assert_eq!(e.kind(), ErrorKind::WouldBlock); | |
372 | } | |
373 | _ => { | |
374 | panic!("Unexpected result for decoder.read"); | |
375 | } | |
376 | } | |
377 | } | |
378 | } |