eazip 0.2.4

An simple yet flexible zip library
Documentation
use std::io::{self, Read, Write};

pub struct Crc32Checker<R> {
    hasher: crc32fast::Hasher,
    expected: u32,
    reader: R,
}

impl<R> Crc32Checker<R> {
    #[inline]
    pub fn new(reader: R, expected: u32) -> Self {
        Self {
            hasher: crc32fast::Hasher::new(),
            expected,
            reader,
        }
    }
}

impl<R: Read> Read for Crc32Checker<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let n = self.reader.read(buf)?;

        if n == 0 && self.hasher.clone().finalize() != self.expected {
            return Err(crc32_check_fail());
        }

        self.hasher.update(&buf[..n]);

        Ok(n)
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        let start = buf.len();
        let res = self.reader.read_to_end(buf);

        self.hasher.update(&buf[start..]);

        if res.is_ok() && self.hasher.clone().finalize() != self.expected {
            return Err(crc32_check_fail());
        }

        res
    }

    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
        let start = buf.len();
        let res = self.reader.read_to_string(buf);

        self.hasher.update(&buf.as_bytes()[start..]);

        if res.is_ok() && self.hasher.clone().finalize() != self.expected {
            return Err(crc32_check_fail());
        }

        res
    }
}

#[cold]
fn crc32_check_fail() -> io::Error {
    io::Error::new(
        io::ErrorKind::InvalidData,
        "invalid CRC32 check: data was corrupted",
    )
}

pub struct Crc32Writer<W> {
    hasher: crc32fast::Hasher,
    writer: W,
}

impl<W: Write> Crc32Writer<W> {
    #[inline]
    pub fn new(writer: W) -> Self {
        Self {
            hasher: crc32fast::Hasher::new(),
            writer,
        }
    }

    #[inline]
    pub fn into_inner(self) -> W {
        self.writer
    }

    #[inline]
    pub fn result(&self) -> u32 {
        self.hasher.clone().finalize()
    }
}

impl<W: Write> Write for Crc32Writer<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = self.writer.write(buf)?;
        self.hasher.update(&buf[..n]);
        Ok(n)
    }

    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
        let n = self.writer.write_vectored(bufs)?;

        let mut left = n;
        for buf in bufs {
            if left == 0 {
                break;
            }
            let size = std::cmp::min(left, buf.len());
            self.hasher.update(&buf[..size]);
            left -= size;
        }

        assert!(left == 0);
        Ok(n)
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        self.writer.flush()
    }
}