mod bits;
mod huffman;
mod lookup;
mod offset_history;
mod window;
use crate::error::Error;
use crate::traits::{Algorithm, Decoder as DecoderTrait, Encoder as EncoderTrait, Progress};
#[derive(Debug, Clone, Copy, Default)]
pub struct Rar1;
impl Algorithm for Rar1 {
const NAME: &'static str = "rar1";
type Encoder = Encoder;
type Decoder = Decoder;
fn encoder() -> Encoder {
Encoder::new()
}
fn decoder() -> Decoder {
Decoder::new()
}
}
#[derive(Debug, Default)]
pub struct Encoder;
impl Encoder {
pub const fn new() -> Self {
Self
}
}
impl EncoderTrait for Encoder {
fn encode(&mut self, _input: &[u8], _output: &mut [u8]) -> Result<Progress, Error> {
Err(Error::Unsupported)
}
fn finish(&mut self, _output: &mut [u8]) -> Result<Progress, Error> {
Err(Error::Unsupported)
}
fn reset(&mut self) {}
}
pub struct Decoder {
unpack_size: Option<u64>,
bit_reader: bits::BitReader,
#[allow(dead_code)]
window: window::Window,
#[allow(dead_code)]
flagtable: lookup::LookupTable,
#[allow(dead_code)]
literaltable: lookup::LookupTable,
#[allow(dead_code)]
offsettable: lookup::LookupTable,
#[allow(dead_code)]
offset_history: offset_history::OffsetHistory,
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for Decoder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("rar1::Decoder")
.field("unpack_size", &self.unpack_size)
.field("bits_buffered", &self.bit_reader.bits_available())
.finish()
}
}
impl Decoder {
pub fn new() -> Self {
Self {
unpack_size: None,
bit_reader: bits::BitReader::new(),
window: window::Window::new(),
flagtable: lookup::LookupTable::new(lookup::LookupKind::Complement, 32),
literaltable: lookup::LookupTable::new(lookup::LookupKind::Identity, 32),
offsettable: lookup::LookupTable::new(lookup::LookupKind::Identity, 32),
offset_history: offset_history::OffsetHistory::new(),
}
}
pub fn with_unpack_size(n: u64) -> Self {
let mut d = Self::new();
d.unpack_size = Some(n);
d
}
pub fn unpack_size(&self) -> Option<u64> {
self.unpack_size
}
}
impl DecoderTrait for Decoder {
fn decode(&mut self, input: &[u8], _output: &mut [u8]) -> Result<Progress, Error> {
if input.is_empty() {
return Ok(Progress {
consumed: 0,
written: 0,
done: false,
});
}
Err(Error::Unsupported)
}
fn finish(&mut self, _output: &mut [u8]) -> Result<Progress, Error> {
if self.bit_reader.bits_available() == 0 && self.window.in_flight() == 0 {
return Ok(Progress {
consumed: 0,
written: 0,
done: true,
});
}
Err(Error::Unsupported)
}
fn reset(&mut self) {
self.unpack_size = None;
self.bit_reader.reset();
self.window.reset();
self.flagtable = lookup::LookupTable::new(lookup::LookupKind::Complement, 32);
self.literaltable = lookup::LookupTable::new(lookup::LookupKind::Identity, 32);
self.offsettable = lookup::LookupTable::new(lookup::LookupKind::Identity, 32);
self.offset_history.reset();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn algorithm_name() {
assert_eq!(<Rar1 as Algorithm>::NAME, "rar1");
}
#[test]
fn encoder_always_unsupported() {
let mut e = Encoder::new();
let mut out = [0u8; 16];
assert_eq!(e.encode(b"hi", &mut out), Err(Error::Unsupported));
assert_eq!(e.finish(&mut out), Err(Error::Unsupported));
e.reset();
}
#[test]
fn decoder_with_unpack_size_records_value() {
let d = Decoder::with_unpack_size(1234);
assert_eq!(d.unpack_size(), Some(1234));
}
#[test]
fn decoder_default_has_no_unpack_size() {
let d = Decoder::new();
assert_eq!(d.unpack_size(), None);
}
#[test]
fn decoder_empty_decode_is_noop() {
let mut d = Decoder::new();
let mut out = [0u8; 4];
let p = d.decode(&[], &mut out).unwrap();
assert_eq!(p, Progress::default());
}
#[test]
fn decoder_nonempty_decode_unsupported() {
let mut d = Decoder::new();
let mut out = [0u8; 16];
assert_eq!(d.decode(&[0xAB], &mut out), Err(Error::Unsupported));
}
#[test]
fn decoder_finish_on_fresh_is_done() {
let mut d = Decoder::new();
let mut out = [0u8; 4];
let p = d.finish(&mut out).unwrap();
assert!(p.done);
assert_eq!(p.written, 0);
}
#[test]
fn decoder_reset_clears_unpack_size() {
let mut d = Decoder::with_unpack_size(42);
d.reset();
assert_eq!(d.unpack_size(), None);
}
}