binhex-rs 0.1.1

Crate to read BinHex 4 encoded files
Documentation
use std::{fs, io};

use crate::{lookup_table, util::ReadByte, Error, SixBitDecoder};

pub struct SixBitRleReader<R> {
    inner: R,

    b6_decoder: SixBitDecoder,
    rle_byte: u8,
    rle_repetition: u8,
    ended: bool,
    pub(crate) data_start: u64,
    pub(crate) position: u64,
}

impl<R> SixBitRleReader<R> {
    pub(crate) fn new(inner: R, data_start: u64) -> Self {
        Self {
            inner,
            b6_decoder: SixBitDecoder::default(),
            ended: false,
            position: 0,
            data_start,
            rle_byte: 0,
            rle_repetition: 0,
        }
    }

    pub(crate) fn into_inner(self) -> R {
        self.inner
    }
}

impl<R: io::Read> SixBitRleReader<R> {
    #[inline]
    fn produce_partial_byte(&mut self) -> io::Result<Option<u8>> {
        loop {
            match lookup_table::TABLE[self.inner.read_byte()? as usize] {
                lookup_table::Entry::Ended => {
                    self.ended = true;
                    return Ok(None);
                }
                lookup_table::Entry::Invalid => return Err(Error::InvalidCharacter.into()),
                lookup_table::Entry::Ignore => continue,
                lookup_table::Entry::Valid(byte) => return Ok(Some(byte)),
            }
        }
    }

    #[inline]
    fn produce_rle_instruction(&mut self) -> io::Result<Option<u8>> {
        loop {
            let Some(partial) = self.produce_partial_byte()? else {
                return Ok(None);
            };

            match self.b6_decoder.next(partial) {
                Some(byte) => return Ok(Some(byte)),
                None => continue,
            }
        }
    }

    #[inline]
    fn produce_byte(&mut self) -> io::Result<Option<u8>> {
        if self.rle_repetition != 0 {
            self.rle_repetition -= 1;
            return Ok(Some(self.rle_byte));
        }

        let Some(current) = self.produce_rle_instruction()? else {
            return Ok(None);
        };

        if current == 0x90 {
            let Some(secondary) = self.produce_rle_instruction()? else {
                return Err(Error::UnexpectedEof.into());
            };

            match secondary {
                0 => self.rle_byte = 0x90,
                1 => self.rle_repetition = 0,
                count => self.rle_repetition = count - 2,
            }
            return Ok(Some(self.rle_byte));
        }

        self.rle_byte = current;
        Ok(Some(current))
    }
}

impl<R: io::Read + io::Seek> SixBitRleReader<R> {
    pub(crate) fn reset(&mut self) -> io::Result<()> {
        self.b6_decoder = SixBitDecoder::new();
        self.rle_byte = 0;
        self.rle_repetition = 0;
        self.ended = false;
        self.inner.seek(io::SeekFrom::Start(self.data_start))?;
        self.position = 0;

        Ok(())
    }
}

impl<R: io::Read> io::Read for SixBitRleReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        if self.ended {
            return Ok(0);
        }

        for (idx, out) in buf.iter_mut().enumerate() {
            let Some(byte) = self.produce_byte()? else {
                self.position += idx as u64;
                return Ok(idx);
            };

            *out = byte;
        }

        self.position += buf.len() as u64;
        Ok(buf.len())
    }
}

impl<R: io::Read + io::Seek> io::Seek for SixBitRleReader<R> {
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
        match pos {
            io::SeekFrom::Start(0) => {
                self.reset()?;
                Ok(0)
            }
            io::SeekFrom::Start(x) => {
                if x < self.position {
                    log::warn!(
                        "Performing expensive backwards seek from {} to {}",
                        self.position,
                        x
                    );
                    self.seek(io::SeekFrom::Start(0))?;
                    self.seek(io::SeekFrom::Current(x as i64))
                } else if x > self.position {
                    self.seek(io::SeekFrom::Current(x as i64 - self.position as i64))
                } else {
                    Ok(x)
                }
            }
            io::SeekFrom::Current(val) if val >= 0 => {
                let mut skipped_bytes = 0;
                for _ in 0..val {
                    if (self.produce_byte()?).is_some() {
                        skipped_bytes += 1;
                    } else {
                        break;
                    }
                }

                self.position += skipped_bytes;
                Ok(self.position)
            }
            mode => {
                eprintln!("Unsupported seek {mode:?}");
                Err(Error::UnsupportedSeek.into())
            }
        }
    }
}

impl<R: Clone> Clone for SixBitRleReader<R> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
            data_start: self.data_start,
            b6_decoder: self.b6_decoder.clone(),
            rle_byte: self.rle_byte,
            rle_repetition: self.rle_repetition,
            ended: self.ended,
            position: self.position,
        }
    }
}

impl SixBitRleReader<fs::File> {
    pub(crate) fn try_clone(&self) -> io::Result<Self> {
        Ok(Self {
            inner: self.inner.try_clone()?,
            data_start: self.data_start,
            b6_decoder: self.b6_decoder.clone(),
            rle_byte: self.rle_byte,
            rle_repetition: self.rle_repetition,
            ended: self.ended,
            position: self.position,
        })
    }
}