macbinary-rs 0.2.0

Transparent access to MacBinary-encoded files
Documentation
use std::io::{self, SeekFrom};

pub struct Reader<R> {
    inner: R,
    start: u64,
    end: u64,
}

impl<R: io::Read + io::Seek> Reader<R> {
    pub fn try_new(mut inner: R, start: u64, end: u64) -> io::Result<Self> {
        assert!(start <= end);
        inner.seek(SeekFrom::Start(start))?;

        Ok(Self { inner, start, end })
    }
}

impl<R: io::Read + io::Seek> io::Read for Reader<R> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let mut position = self.inner.stream_position()?;

        // Try to correct faulty position
        if position < self.start {
            position = self.inner.seek(SeekFrom::Start(self.start))?;
        }

        // Report error if position could not be corrected
        if position < self.start {
            return Err(io::Error::new(
                io::ErrorKind::Unsupported,
                "Can not read before start of substream",
            ));
        }

        // No more bytes available
        if position >= self.end {
            return Ok(0);
        }

        let available_bytes = self.end - position;

        // Enough bytes should be available
        if available_bytes >= buf.len() as u64 {
            return self.inner.read(buf);
        }

        // Cap reading at end of substream
        self.inner.read(&mut buf[0..(available_bytes as usize)])
    }
}

impl<R: io::Seek> io::Seek for Reader<R> {
    #[inline]
    fn stream_len(&mut self) -> io::Result<u64> {
        Ok(self.end - self.start)
    }

    #[inline]
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        match pos {
            SeekFrom::Start(offset) => {
                let result = self.inner.seek(SeekFrom::Start(self.start + offset))?;
                if self.start <= result {
                    return Ok(result - self.start);
                }

                Ok(self.end)
            }
            SeekFrom::End(end) => {
                let target = self.end as i64 + end;
                if target < 0 {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "Can not seek before start of the stream",
                    ));
                }
                self.seek(SeekFrom::Start(target as u64))
            }
            SeekFrom::Current(relative) => {
                let current = self.stream_position()?;
                let new = current as i64 + relative;
                if new < 0 {
                    return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "Can not seek before start of the stream",
                    ));
                }

                self.seek(SeekFrom::Start(new as u64))
            }
        }
    }

    #[inline]
    fn stream_position(&mut self) -> io::Result<u64> {
        let pos = self.inner.stream_position()?;
        if self.start <= pos {
            return Ok(pos - self.start);
        }

        Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Can not seek before start of the stream",
        ))
    }
}

#[cfg(test)]
mod test {
    use std::io::{self, Read};

    use crate::Reader;

    #[test]
    fn empty() {
        let mut buffer = [0xAB; 12];

        let inner = io::Cursor::new(b"");
        let mut reader = Reader::try_new(inner, 0, 0).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(0)));

        let inner = io::Cursor::new(b"");
        let mut reader = Reader::try_new(inner, 0, 10).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(0)));

        let inner = io::Cursor::new(b"");
        let mut reader = Reader::try_new(inner, 5, 10).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(0)));
    }

    #[test]
    fn simple() {
        let mut buffer = [0xAB; 1];
        let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
        let mut reader = Reader::try_new(inner, 1, 2).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(1)));
        assert_eq!(buffer, [0x02]);

        let mut buffer = [0xAB; 3];
        let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
        let mut reader = Reader::try_new(inner, 2, 5).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(3)));
        assert_eq!(buffer, [0x03, 0x04, 0x05]);
    }

    #[test]
    fn capping() {
        let mut buffer = [0xAB; 3];
        let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
        let mut reader = Reader::try_new(inner, 1, 1).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(0)));

        let mut buffer = [0xAB; 3];
        let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
        let mut reader = Reader::try_new(inner, 1, 2).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(1)));
        assert_eq!(buffer, [0x02, 0xAB, 0xAB]);

        let mut buffer = [0xAB; 3];
        let inner = io::Cursor::new(b"\x01\x02\x03\x04\x05");
        let mut reader = Reader::try_new(inner, 1, 3).unwrap();
        assert!(matches!(reader.read(&mut buffer), Ok(2)));
        assert_eq!(buffer, [0x02, 0x03, 0xAB]);
    }
}