cloudflare-soos 2.3.1

Helper tool for Cloudflare's enhanced HTTP/2 and HTTP/3 prioritization, which makes progressive images load faster. Supports JPEG, GIF, and PNG.
Documentation
use std::io::BufRead;
use std::mem;
use super::common::{Block, Frame};

mod decoder;
pub use self::decoder::{
    Decoded, DecodingError, StreamingDecoder,
};

pub fn read_info<R: BufRead>(r: R) -> Result<Decoder<R>, DecodingError> {
    Decoder::with_no_init(r, StreamingDecoder::with_options()).init()
}

struct ReadDecoder<R: BufRead> {
    reader: R,
    decoder: StreamingDecoder,
    at_eof: bool,
}

impl<R: BufRead> ReadDecoder<R> {
    fn decode_next(&mut self) -> Result<Option<Decoded>, DecodingError> {
        while !self.at_eof {
            let (consumed, result) = {
                let buf = self.reader.fill_buf()?;
                if buf.is_empty() {
                    return Err(DecodingError::format(
                        "unexpected EOF"
                    ))
                }
                self.decoder.update(buf)?
            };
            self.reader.consume(consumed);
            match result {
                Decoded::Nothing => (),
                Decoded::BlockStart(Block::Trailer) => {
                    self.at_eof = true
                },
                result => return Ok(unsafe{
                    // FIXME: #6393
                    Some(mem::transmute::<Decoded, Decoded>(result))
                }),
            }
        }
        Ok(None)
    }
}

#[allow(dead_code)]
/// GIF decoder
pub struct Decoder<R: BufRead> {
    decoder: ReadDecoder<R>,
    current_frame: Frame,
}

impl<R> Decoder<R> where R: BufRead {
    fn with_no_init(reader: R, decoder: StreamingDecoder) -> Decoder<R> {
        Decoder {
            decoder: ReadDecoder {
                reader,
                decoder,
                at_eof: false
            },
            current_frame: Frame::default(),
        }
    }
    
    fn init(mut self) -> Result<Self, DecodingError> {
        loop {
            match self.decoder.decode_next()? {
                Some(Decoded::BackgroundColor) => {}
                Some(Decoded::GlobalPalette) => {
                    break
                },
                Some(_) => {
                    // Unreachable since this loop exists after the global
                    // palette has been read.
                    unreachable!()
                },
                None => return Err(DecodingError::format(
                    "file does not contain any image data"
                ))
            }
        }
        Ok(self)
    }
    
    /// Returns the next frame info
    pub fn next_frame_info(&mut self) -> Result<Option<&Frame>, DecodingError> {
        loop {
            match self.decoder.decode_next()? {
                Some(Decoded::Frame(frame)) => {
                    self.current_frame = frame.clone();
                    break
                },
                Some(_) => (),
                None => return Ok(None)
                
            }
        }
        Ok(Some(&self.current_frame))
    }

    /// Reads the data of the current frame into a pre-allocated buffer.
    ///
    /// `Self::next_frame_info` needs to be called beforehand.
    /// The length of `buf` must be at least `Self::buffer_size`.
    /// Deinterlaces the result.
    pub fn read_into_buffer(&mut self) -> Result<(), DecodingError> {
        loop {
            match self.decoder.decode_next()? {
                Some(Decoded::SomeData) => {
                    continue
                },
                Some(_) | // make sure that no important result is missed
                None => return Ok(()),
            }
        }
    }
}