#![crate_type = "bin"]
#[macro_use] extern crate log;
extern crate compress;
extern crate byteorder;
use std::collections::HashMap;
use std::io::{self, Read, Write};
use std::{env, str};
use compress::{bwt, lz4, ReadExact};
use compress::entropy::ari;
use byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt};
static MAGIC : u32 = 0x73632172;
struct Config {
exe_name: String,
methods: Vec<String>,
block_size: usize,
decompress: bool,
}
impl Config {
fn query<I>(mut args: I) -> Config where I: Iterator<Item = String> + Sized {
let mut cfg = Config {
exe_name: args.next().unwrap().clone(),
methods: Vec::new(),
block_size: 1<<16,
decompress: false,
};
let mut handlers: HashMap<&str, Box<dyn FnMut(&str, &mut Config)>> =
HashMap::new();
handlers.insert("d", Box::new(|_, cfg| { cfg.decompress = true; }));
handlers.insert("block", Box::new(|b, cfg| {
cfg.block_size = b.parse().unwrap();
}));
for arg in args {
let slice = &arg[..];
if slice.starts_with("-") {
match handlers.iter_mut().find(|&(&k,_)| slice[1..].starts_with(k)) {
Some((k,h)) => (*h)(&slice[1+k.len()..], &mut cfg),
None => println!("Warning: unrecognized option: {}", &arg[..]),
}
}else {
cfg.methods.push(arg.to_string());
}
}
cfg
}
}
struct Pass {
encode: Box<dyn FnMut(Box<dyn Write + 'static>, &Config)
-> Box<dyn Write + 'static> + 'static>,
decode: Box<dyn FnMut(Box<dyn Read + 'static>, &Config)
-> Box<dyn Read + 'static> + 'static>,
info: String,
}
pub fn main() {
let mut passes: HashMap<String,Pass> = HashMap::new();
passes.insert("dummy".to_string(), Pass {
encode: Box::new(|w,_| w),
decode: Box::new(|r,_| r),
info: "pass-through".to_string(),
});
passes.insert("ari".to_string(), Pass {
encode: Box::new(|w,_c| {
Box::new(ari::ByteEncoder::new(w)) as Box<dyn Write + 'static>
}),
decode: Box::new(|r,_c| {
Box::new(ari::ByteDecoder::new(r)) as Box<dyn Read + 'static>
}),
info: "Adaptive arithmetic byte coder".to_string(),
});
passes.insert("bwt".to_string(), Pass {
encode: Box::new(|w,c| {
Box::new(bwt::Encoder::new(w, c.block_size)) as Box<dyn Write + 'static>
}),
decode: Box::new(|r,_c| {
Box::new(bwt::Decoder::new(r, true)) as Box<dyn Read + 'static>
}),
info: "Burrows-Wheeler Transformation".to_string(),
});
passes.insert("mtf".to_string(), Pass {
encode: Box::new(|w,_c| {
Box::new(bwt::mtf::Encoder::new(w)) as Box<dyn Write + 'static>
}),
decode: Box::new(|r,_c| {
Box::new(bwt::mtf::Decoder::new(r)) as Box<dyn Read + 'static>
}),
info: "Move-To-Front Transformation".to_string(),
});
passes.insert("lz4".to_string(), Pass {
encode: Box::new(|w,_c| {
Box::new(lz4::Encoder::new(w)) as Box<dyn Write + 'static>
}),
decode: Box::new(|r,_c| { Box::new(lz4::Decoder::new(r)) as Box<dyn Read + 'static>
}),
info: "Ziv-Lempel derivative, focused at speed".to_string(),
});
let config = Config::query(env::args());
let mut input = io::stdin();
let mut output = io::stdout();
if config.decompress {
assert!(config.methods.is_empty(), "Decompression methods are set in stone");
match input.read_u32::<LittleEndian>() {
Ok(magic) if magic != MAGIC => {
error!("Input is not a rust-compress archive");
return
},
Err(e) => {
error!("Unable to read input: {:?}", e);
return
},
_ => () }
let methods: Vec<_> = (0..(input.read_u8().unwrap() as usize)).map(|_| {
let len = input.read_u8().unwrap() as u64;
let mut bytes = Vec::new();
input.push_exactly(len, &mut bytes).unwrap();
str::from_utf8(&bytes[..]).unwrap().to_string()
}).collect();
let mut rsum: Box<dyn Read> = Box::new(input);
for met in methods.iter() {
info!("Found pass {}", *met);
match passes.get_mut(met) {
Some(pa) => rsum = (pa.decode)(rsum, &config),
None => panic!("Pass is not implemented"),
}
}
io::copy(&mut rsum, &mut output).unwrap();
}else if config.methods.is_empty() {
println!("rust-compress test application");
println!("Usage:");
println!("\t{} <options> <method1> .. <methodN> <input >output", config.exe_name);
println!("Options:");
println!("\t-d (to decompress)");
println!("\t-block<N> (BWT block size)");
println!("Passes:");
for (name,pa) in passes.iter() {
println!("\t{} = {}", *name, pa.info);
}
}else {
output.write_u32::<LittleEndian>(MAGIC).unwrap();
output.write_u8(config.methods.len() as u8).unwrap();
for met in config.methods.iter() {
output.write_u8(met.len() as u8).unwrap();
output.write_all(met.as_bytes()).unwrap();
}
let mut wsum: Box<dyn Write> = Box::new(output);
for met in config.methods.iter() {
match passes.get_mut(met) {
Some(pa) => wsum = (pa.encode)(wsum, &config),
None => panic!("Pass {} is not implemented", *met)
}
}
io::copy(&mut input, &mut wsum).unwrap();
wsum.flush().unwrap();
}
}