1 extern crate crc32fast;
2 
3 use std::borrow::Cow;
4 use std::cmp::min;
5 use std::convert::From;
6 use std::default::Default;
7 use std::error;
8 use std::fmt;
9 use std::io;
10 
11 use crc32fast::Hasher as Crc32;
12 
13 use super::zlib::ZlibStream;
14 use crate::chunk::{self, ChunkType, IDAT, IEND, IHDR};
15 use crate::common::{
16     AnimationControl, BitDepth, BlendOp, ColorType, DisposeOp, FrameControl, Info, PixelDimensions,
17     Unit,
18 };
19 use crate::traits::ReadBytesExt;
20 
21 /// TODO check if these size are reasonable
22 pub const CHUNCK_BUFFER_SIZE: usize = 32 * 1024;
23 
24 /// Determines if checksum checks should be disabled globally.
25 ///
26 /// This is used only in fuzzing. `afl` automatically adds `--cfg fuzzing` to RUSTFLAGS which can
27 /// be used to detect that build.
28 const CHECKSUM_DISABLED: bool = cfg!(fuzzing);
29 
30 #[derive(Debug)]
31 enum U32Value {
32     // CHUNKS
33     Length,
34     Type(u32),
35     Crc(ChunkType),
36 }
37 
38 #[derive(Debug)]
39 enum State {
40     Signature(u8, [u8; 7]),
41     U32Byte3(U32Value, u32),
42     U32Byte2(U32Value, u32),
43     U32Byte1(U32Value, u32),
44     U32(U32Value),
45     ReadChunk(ChunkType, bool),
46     PartialChunk(ChunkType),
47     DecodeData(ChunkType, usize),
48 }
49 
50 #[derive(Debug)]
51 /// Result of the decoding process
52 pub enum Decoded {
53     /// Nothing decoded yet
54     Nothing,
55     Header(u32, u32, BitDepth, ColorType, bool),
56     ChunkBegin(u32, ChunkType),
57     ChunkComplete(u32, ChunkType),
58     PixelDimensions(PixelDimensions),
59     AnimationControl(AnimationControl),
60     FrameControl(FrameControl),
61     /// Decoded raw image data.
62     ImageData,
63     /// The last of a consecutive chunk of IDAT was done.
64     /// This is distinct from ChunkComplete which only marks that some IDAT chunk was completed but
65     /// not that no additional IDAT chunk follows.
66     ImageDataFlushed,
67     PartialChunk(ChunkType),
68     ImageEnd,
69 }
70 
71 #[derive(Debug)]
72 pub enum DecodingError {
73     IoError(io::Error),
74     Format(Cow<'static, str>),
75     InvalidSignature,
76     CrcMismatch {
77         /// bytes to skip to try to recover from this error
78         recover: usize,
79         /// Stored CRC32 value
80         crc_val: u32,
81         /// Calculated CRC32 sum
82         crc_sum: u32,
83         chunk: ChunkType,
84     },
85     Other(Cow<'static, str>),
86     CorruptFlateStream,
87     LimitsExceeded,
88 }
89 
90 impl error::Error for DecodingError {
cause(&self) -> Option<&(dyn error::Error + 'static)>91     fn cause(&self) -> Option<&(dyn error::Error + 'static)> {
92         match self {
93             DecodingError::IoError(err) => Some(err),
94             _ => None,
95         }
96     }
97 }
98 
99 impl fmt::Display for DecodingError {
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>100     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
101         use self::DecodingError::*;
102         match self {
103             IoError(err) => write!(fmt, "{}", err),
104             Format(desc) | Other(desc) => write!(fmt, "{}", &desc),
105             InvalidSignature => write!(fmt, "invalid signature"),
106             CrcMismatch { .. } => write!(fmt, "CRC error"),
107             CorruptFlateStream => write!(fmt, "compressed data stream corrupted"),
108             LimitsExceeded => write!(fmt, "limits are exceeded"),
109         }
110     }
111 }
112 
113 impl From<io::Error> for DecodingError {
from(err: io::Error) -> DecodingError114     fn from(err: io::Error) -> DecodingError {
115         DecodingError::IoError(err)
116     }
117 }
118 
119 impl From<String> for DecodingError {
from(err: String) -> DecodingError120     fn from(err: String) -> DecodingError {
121         DecodingError::Other(err.into())
122     }
123 }
124 
125 impl From<DecodingError> for io::Error {
from(err: DecodingError) -> io::Error126     fn from(err: DecodingError) -> io::Error {
127         match err {
128             DecodingError::IoError(err) => err,
129             err => io::Error::new(io::ErrorKind::Other, err.to_string()),
130         }
131     }
132 }
133 
134 /// PNG StreamingDecoder (low-level interface)
135 pub struct StreamingDecoder {
136     state: Option<State>,
137     current_chunk: ChunkState,
138     /// The inflater state handling consecutive `IDAT` and `fdAT` chunks.
139     inflater: ZlibStream,
140     /// The complete image info read from all prior chunks.
141     info: Option<Info>,
142     /// The animation chunk sequence number.
143     current_seq_no: Option<u32>,
144     /// Stores where in decoding an `fdAT` chunk we are.
145     apng_seq_handled: bool,
146     have_idat: bool,
147 }
148 
149 struct ChunkState {
150     /// The type of the current chunk.
151     /// Relevant for `IDAT` and `fdAT` which aggregate consecutive chunks of their own type.
152     type_: ChunkType,
153 
154     /// Partial crc until now.
155     crc: Crc32,
156 
157     /// Remanining bytes to be read.
158     remaining: u32,
159 
160     /// Non-decoded bytes in the chunk.
161     raw_bytes: Vec<u8>,
162 }
163 
164 impl StreamingDecoder {
165     /// Creates a new StreamingDecoder
166     ///
167     /// Allocates the internal buffers.
new() -> StreamingDecoder168     pub fn new() -> StreamingDecoder {
169         StreamingDecoder {
170             state: Some(State::Signature(0, [0; 7])),
171             current_chunk: ChunkState::default(),
172             inflater: ZlibStream::new(),
173             info: None,
174             current_seq_no: None,
175             apng_seq_handled: false,
176             have_idat: false,
177         }
178     }
179 
180     /// Resets the StreamingDecoder
reset(&mut self)181     pub fn reset(&mut self) {
182         self.state = Some(State::Signature(0, [0; 7]));
183         self.current_chunk.crc = Crc32::new();
184         self.current_chunk.remaining = 0;
185         self.current_chunk.raw_bytes.clear();
186         self.inflater = ZlibStream::new();
187         self.info = None;
188         self.current_seq_no = None;
189         self.apng_seq_handled = false;
190         self.have_idat = false;
191     }
192 
193     /// Low level StreamingDecoder interface.
194     ///
195     /// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have
196     /// been consumed from the input buffer and the current decoding result. If the decoded chunk
197     /// was an image data chunk, it also appends the read data to `image_data`.
update( &mut self, mut buf: &[u8], image_data: &mut Vec<u8>, ) -> Result<(usize, Decoded), DecodingError>198     pub fn update(
199         &mut self,
200         mut buf: &[u8],
201         image_data: &mut Vec<u8>,
202     ) -> Result<(usize, Decoded), DecodingError> {
203         let len = buf.len();
204         while !buf.is_empty() && self.state.is_some() {
205             match self.next_state(buf, image_data) {
206                 Ok((bytes, Decoded::Nothing)) => buf = &buf[bytes..],
207                 Ok((bytes, result)) => {
208                     buf = &buf[bytes..];
209                     return Ok((len - buf.len(), result));
210                 }
211                 Err(err) => return Err(err),
212             }
213         }
214         Ok((len - buf.len(), Decoded::Nothing))
215     }
216 
next_state<'a>( &'a mut self, buf: &[u8], image_data: &mut Vec<u8>, ) -> Result<(usize, Decoded), DecodingError>217     fn next_state<'a>(
218         &'a mut self,
219         buf: &[u8],
220         image_data: &mut Vec<u8>,
221     ) -> Result<(usize, Decoded), DecodingError> {
222         use self::State::*;
223 
224         macro_rules! goto (
225             ($n:expr, $state:expr) => ({
226                 self.state = Some($state);
227                 Ok(($n, Decoded::Nothing))
228             });
229             ($state:expr) => ({
230                 self.state = Some($state);
231                 Ok((1, Decoded::Nothing))
232             });
233             ($n:expr, $state:expr, emit $res:expr) => ({
234                 self.state = Some($state);
235                 Ok(($n, $res))
236             });
237             ($state:expr, emit $res:expr) => ({
238                 self.state = Some($state);
239                 Ok((1, $res))
240             })
241         );
242 
243         let current_byte = buf[0];
244 
245         // Driver should ensure that state is never None
246         let state = self.state.take().unwrap();
247 
248         match state {
249             Signature(i, mut signature) if i < 7 => {
250                 signature[i as usize] = current_byte;
251                 goto!(Signature(i + 1, signature))
252             }
253             Signature(_, signature)
254                 if signature == [137, 80, 78, 71, 13, 10, 26] && current_byte == 10 =>
255             {
256                 goto!(U32(U32Value::Length))
257             }
258             Signature(..) => Err(DecodingError::InvalidSignature),
259             U32Byte3(type_, mut val) => {
260                 use self::U32Value::*;
261                 val |= u32::from(current_byte);
262                 match type_ {
263                     Length => goto!(U32(Type(val))),
264                     Type(length) => {
265                         let type_str = [
266                             (val >> 24) as u8,
267                             (val >> 16) as u8,
268                             (val >> 8) as u8,
269                             val as u8,
270                         ];
271                         if type_str != self.current_chunk.type_
272                             && (self.current_chunk.type_ == IDAT
273                                 || self.current_chunk.type_ == chunk::fdAT)
274                         {
275                             self.current_chunk.type_ = type_str;
276                             self.inflater.finish_compressed_chunks(image_data)?;
277                             self.inflater.reset();
278                             return goto!(
279                                 0,
280                                 U32Byte3(Type(length), val & !0xff),
281                                 emit Decoded::ImageDataFlushed
282                             );
283                         }
284                         self.current_chunk.type_ = type_str;
285                         self.current_chunk.crc.reset();
286                         self.current_chunk.crc.update(&type_str);
287                         self.current_chunk.remaining = length;
288                         self.apng_seq_handled = false;
289                         goto!(
290                             ReadChunk(type_str, true),
291                             emit Decoded::ChunkBegin(length, type_str)
292                         )
293                     }
294                     Crc(type_str) => {
295                         let sum = self.current_chunk.crc.clone().finalize();
296                         if CHECKSUM_DISABLED || val == sum {
297                             goto!(
298                                 State::U32(U32Value::Length),
299                                 emit if type_str == IEND {
300                                     Decoded::ImageEnd
301                                 } else {
302                                     Decoded::ChunkComplete(val, type_str)
303                                 }
304                             )
305                         } else {
306                             Err(DecodingError::CrcMismatch {
307                                 recover: 1,
308                                 crc_val: val,
309                                 crc_sum: sum,
310                                 chunk: type_str,
311                             })
312                         }
313                     }
314                 }
315             }
316             U32Byte2(type_, val) => goto!(U32Byte3(type_, val | u32::from(current_byte) << 8)),
317             U32Byte1(type_, val) => goto!(U32Byte2(type_, val | u32::from(current_byte) << 16)),
318             U32(type_) => goto!(U32Byte1(type_, u32::from(current_byte) << 24)),
319             PartialChunk(type_str) => {
320                 match type_str {
321                     IDAT => {
322                         self.have_idat = true;
323                         goto!(
324                             0,
325                             DecodeData(type_str, 0),
326                             emit Decoded::PartialChunk(type_str)
327                         )
328                     }
329                     chunk::fdAT => {
330                         let data_start;
331                         if let Some(seq_no) = self.current_seq_no {
332                             if !self.apng_seq_handled {
333                                 data_start = 4;
334                                 let mut buf = &self.current_chunk.raw_bytes[..];
335                                 let next_seq_no = buf.read_be()?;
336                                 if next_seq_no != seq_no + 1 {
337                                     return Err(DecodingError::Format(
338                                         format!(
339                                             "Sequence is not in order, expected #{} got #{}.",
340                                             seq_no + 1,
341                                             next_seq_no
342                                         )
343                                         .into(),
344                                     ));
345                                 }
346                                 self.current_seq_no = Some(next_seq_no);
347                                 self.apng_seq_handled = true;
348                             } else {
349                                 data_start = 0;
350                             }
351                         } else {
352                             return Err(DecodingError::Format(
353                                 "fcTL chunk missing before fdAT chunk.".into(),
354                             ));
355                         }
356                         goto!(
357                             0,
358                             DecodeData(type_str, data_start),
359                             emit Decoded::PartialChunk(type_str)
360                         )
361                     }
362                     // Handle other chunks
363                     _ => {
364                         if self.current_chunk.remaining == 0 {
365                             // complete chunk
366                             Ok((0, self.parse_chunk(type_str)?))
367                         } else {
368                             goto!(
369                                 0, ReadChunk(type_str, true),
370                                 emit Decoded::PartialChunk(type_str)
371                             )
372                         }
373                     }
374                 }
375             }
376             ReadChunk(type_str, clear) => {
377                 if clear {
378                     self.current_chunk.raw_bytes.clear();
379                 }
380                 if self.current_chunk.remaining > 0 {
381                     let ChunkState {
382                         crc,
383                         remaining,
384                         raw_bytes,
385                         type_: _,
386                     } = &mut self.current_chunk;
387                     let buf_avail = raw_bytes.capacity() - raw_bytes.len();
388                     let bytes_avail = min(buf.len(), buf_avail);
389                     let n = min(*remaining, bytes_avail as u32);
390                     if buf_avail == 0 {
391                         goto!(0, PartialChunk(type_str))
392                     } else {
393                         let buf = &buf[..n as usize];
394                         crc.update(buf);
395                         raw_bytes.extend_from_slice(buf);
396                         *remaining -= n;
397                         if *remaining == 0 {
398                             goto!(n as usize, PartialChunk(type_str))
399                         } else {
400                             goto!(n as usize, ReadChunk(type_str, false))
401                         }
402                     }
403                 } else {
404                     goto!(0, U32(U32Value::Crc(type_str)))
405                 }
406             }
407             DecodeData(type_str, mut n) => {
408                 let chunk_len = self.current_chunk.raw_bytes.len();
409                 let chunk_data = &self.current_chunk.raw_bytes[n..];
410                 let c = self.inflater.decompress(chunk_data, image_data)?;
411                 n += c;
412                 if n == chunk_len && c == 0 {
413                     goto!(
414                         0,
415                         ReadChunk(type_str, true),
416                         emit Decoded::ImageData
417                     )
418                 } else {
419                     goto!(
420                         0,
421                         DecodeData(type_str, n),
422                         emit Decoded::ImageData
423                     )
424                 }
425             }
426         }
427     }
428 
parse_chunk(&mut self, type_str: [u8; 4]) -> Result<Decoded, DecodingError>429     fn parse_chunk(&mut self, type_str: [u8; 4]) -> Result<Decoded, DecodingError> {
430         self.state = Some(State::U32(U32Value::Crc(type_str)));
431         if self.info.is_none() && type_str != IHDR {
432             return Err(DecodingError::Format(
433                 format!(
434                     "{} chunk appeared before IHDR chunk",
435                     String::from_utf8_lossy(&type_str)
436                 )
437                 .into(),
438             ));
439         }
440         match match type_str {
441             IHDR => self.parse_ihdr(),
442             chunk::PLTE => self.parse_plte(),
443             chunk::tRNS => self.parse_trns(),
444             chunk::pHYs => self.parse_phys(),
445             chunk::acTL => self.parse_actl(),
446             chunk::fcTL => self.parse_fctl(),
447             _ => Ok(Decoded::PartialChunk(type_str)),
448         } {
449             Err(err) => {
450                 // Borrow of self ends here, because Decoding error does not borrow self.
451                 self.state = None;
452                 Err(err)
453             }
454             ok => ok,
455         }
456     }
457 
get_info_or_err(&self) -> Result<&Info, DecodingError>458     fn get_info_or_err(&self) -> Result<&Info, DecodingError> {
459         self.info
460             .as_ref()
461             .ok_or_else(|| DecodingError::Format("IHDR chunk missing".into()))
462     }
463 
parse_fctl(&mut self) -> Result<Decoded, DecodingError>464     fn parse_fctl(&mut self) -> Result<Decoded, DecodingError> {
465         let mut buf = &self.current_chunk.raw_bytes[..];
466         let next_seq_no = buf.read_be()?;
467 
468         // Asuming that fcTL is required before *every* fdAT-sequence
469         self.current_seq_no = Some(if let Some(seq_no) = self.current_seq_no {
470             if next_seq_no != seq_no + 1 {
471                 return Err(DecodingError::Format(
472                     format!(
473                         "Sequence is not in order, expected #{} got #{}.",
474                         seq_no + 1,
475                         next_seq_no
476                     )
477                     .into(),
478                 ));
479             }
480             next_seq_no
481         } else {
482             if next_seq_no != 0 {
483                 return Err(DecodingError::Format(
484                     format!(
485                         "Sequence is not in order, expected #{} got #{}.",
486                         0, next_seq_no
487                     )
488                     .into(),
489                 ));
490             }
491             0
492         });
493         self.inflater = ZlibStream::new();
494         let fc = FrameControl {
495             sequence_number: next_seq_no,
496             width: buf.read_be()?,
497             height: buf.read_be()?,
498             x_offset: buf.read_be()?,
499             y_offset: buf.read_be()?,
500             delay_num: buf.read_be()?,
501             delay_den: buf.read_be()?,
502             dispose_op: match DisposeOp::from_u8(buf.read_be()?) {
503                 Some(dispose_op) => dispose_op,
504                 None => return Err(DecodingError::Format("invalid dispose operation".into())),
505             },
506             blend_op: match BlendOp::from_u8(buf.read_be()?) {
507                 Some(blend_op) => blend_op,
508                 None => return Err(DecodingError::Format("invalid blend operation".into())),
509             },
510         };
511         self.info.as_ref().unwrap().validate(&fc)?;
512         self.info.as_mut().unwrap().frame_control = Some(fc);
513         Ok(Decoded::FrameControl(fc))
514     }
515 
parse_actl(&mut self) -> Result<Decoded, DecodingError>516     fn parse_actl(&mut self) -> Result<Decoded, DecodingError> {
517         if self.have_idat {
518             Err(DecodingError::Format(
519                 "acTL chunk appeared after first IDAT chunk".into(),
520             ))
521         } else {
522             let mut buf = &self.current_chunk.raw_bytes[..];
523             let actl = AnimationControl {
524                 num_frames: buf.read_be()?,
525                 num_plays: buf.read_be()?,
526             };
527             self.info.as_mut().unwrap().animation_control = Some(actl);
528             Ok(Decoded::AnimationControl(actl))
529         }
530     }
531 
parse_plte(&mut self) -> Result<Decoded, DecodingError>532     fn parse_plte(&mut self) -> Result<Decoded, DecodingError> {
533         if let Some(info) = self.info.as_mut() {
534             info.palette = Some(self.current_chunk.raw_bytes.clone())
535         }
536         Ok(Decoded::Nothing)
537     }
538 
parse_trns(&mut self) -> Result<Decoded, DecodingError>539     fn parse_trns(&mut self) -> Result<Decoded, DecodingError> {
540         use crate::common::ColorType::*;
541         let (color_type, bit_depth) = {
542             let info = self.get_info_or_err()?;
543             (info.color_type, info.bit_depth as u8)
544         };
545         let vec = self.current_chunk.raw_bytes.clone();
546         let len = vec.len();
547         let info = match self.info {
548             Some(ref mut info) => info,
549             None => {
550                 return Err(DecodingError::Format(
551                     "tRNS chunk occured before IHDR chunk".into(),
552                 ))
553             }
554         };
555         info.trns = Some(vec);
556         let vec = info.trns.as_mut().unwrap();
557         match color_type {
558             Grayscale => {
559                 if len < 2 {
560                     return Err(DecodingError::Format("not enough palette entries".into()));
561                 }
562                 if bit_depth < 16 {
563                     vec[0] = vec[1];
564                     vec.truncate(1);
565                 }
566                 Ok(Decoded::Nothing)
567             }
568             RGB => {
569                 if len < 6 {
570                     return Err(DecodingError::Format("not enough palette entries".into()));
571                 }
572                 if bit_depth < 16 {
573                     vec[0] = vec[1];
574                     vec[1] = vec[3];
575                     vec[2] = vec[5];
576                     vec.truncate(3);
577                 }
578                 Ok(Decoded::Nothing)
579             }
580             Indexed => {
581                 let _ = info.palette.as_ref().ok_or_else(|| {
582                     DecodingError::Format("tRNS chunk occured before PLTE chunk".into())
583                 });
584                 Ok(Decoded::Nothing)
585             }
586             c => Err(DecodingError::Format(
587                 format!("tRNS chunk found for color type ({})", c as u8).into(),
588             )),
589         }
590     }
591 
parse_phys(&mut self) -> Result<Decoded, DecodingError>592     fn parse_phys(&mut self) -> Result<Decoded, DecodingError> {
593         if self.have_idat {
594             Err(DecodingError::Format(
595                 "pHYs chunk appeared after first IDAT chunk".into(),
596             ))
597         } else {
598             let mut buf = &self.current_chunk.raw_bytes[..];
599             let xppu = buf.read_be()?;
600             let yppu = buf.read_be()?;
601             let unit = buf.read_be()?;
602             let unit = match Unit::from_u8(unit) {
603                 Some(unit) => unit,
604                 None => {
605                     return Err(DecodingError::Format(
606                         format!("invalid unit ({})", unit).into(),
607                     ))
608                 }
609             };
610             let pixel_dims = PixelDimensions { xppu, yppu, unit };
611             self.info.as_mut().unwrap().pixel_dims = Some(pixel_dims);
612             Ok(Decoded::PixelDimensions(pixel_dims))
613         }
614     }
615 
parse_ihdr(&mut self) -> Result<Decoded, DecodingError>616     fn parse_ihdr(&mut self) -> Result<Decoded, DecodingError> {
617         // TODO: check if color/bit depths combination is valid
618         let mut buf = &self.current_chunk.raw_bytes[..];
619         let width = buf.read_be()?;
620         let height = buf.read_be()?;
621         let bit_depth = buf.read_be()?;
622         let bit_depth = match BitDepth::from_u8(bit_depth) {
623             Some(bits) => bits,
624             None => {
625                 return Err(DecodingError::Format(
626                     format!("invalid bit depth ({})", bit_depth).into(),
627                 ))
628             }
629         };
630         let color_type = buf.read_be()?;
631         let color_type = match ColorType::from_u8(color_type) {
632             Some(color_type) => color_type,
633             None => {
634                 return Err(DecodingError::Format(
635                     format!("invalid color type ({})", color_type).into(),
636                 ))
637             }
638         };
639         match buf.read_be()? {
640             // compression method
641             0u8 => (),
642             n => {
643                 return Err(DecodingError::Format(
644                     format!("unknown compression method ({})", n).into(),
645                 ))
646             }
647         }
648         match buf.read_be()? {
649             // filter method
650             0u8 => (),
651             n => {
652                 return Err(DecodingError::Format(
653                     format!("unknown filter method ({})", n).into(),
654                 ))
655             }
656         }
657         let interlaced = match buf.read_be()? {
658             0u8 => false,
659             1 => true,
660             n => {
661                 return Err(DecodingError::Format(
662                     format!("unknown interlace method ({})", n).into(),
663                 ))
664             }
665         };
666         let mut info = Info::default();
667 
668         info.width = width;
669         info.height = height;
670         info.bit_depth = bit_depth;
671         info.color_type = color_type;
672         info.interlaced = interlaced;
673         self.info = Some(info);
674         Ok(Decoded::Header(
675             width, height, bit_depth, color_type, interlaced,
676         ))
677     }
678 }
679 
680 impl Info {
validate(&self, fc: &FrameControl) -> Result<(), DecodingError>681     fn validate(&self, fc: &FrameControl) -> Result<(), DecodingError> {
682         // Validate mathematically: fc.width + fc.x_offset <= self.width
683         let in_x_bounds = Some(fc.width) <= self.width.checked_sub(fc.x_offset);
684         // Validate mathematically: fc.height + fc.y_offset <= self.height
685         let in_y_bounds = Some(fc.height) <= self.height.checked_sub(fc.y_offset);
686 
687         if !in_x_bounds || !in_y_bounds {
688             return Err(DecodingError::Format("Sub frame is out-of-bounds".into()));
689         }
690 
691         Ok(())
692     }
693 }
694 
695 impl Default for StreamingDecoder {
default() -> Self696     fn default() -> Self {
697         Self::new()
698     }
699 }
700 
701 impl Default for ChunkState {
default() -> Self702     fn default() -> Self {
703         ChunkState {
704             type_: [0; 4],
705             crc: Crc32::new(),
706             remaining: 0,
707             raw_bytes: Vec::with_capacity(CHUNCK_BUFFER_SIZE),
708         }
709     }
710 }
711 
712 #[inline(always)]
get_info(d: &StreamingDecoder) -> Option<&Info>713 pub fn get_info(d: &StreamingDecoder) -> Option<&Info> {
714     d.info.as_ref()
715 }
716