blob: 7392e323ceeffd5b7eeedafa68d4d61f028c4cac [file] [edit]
#[cfg(feature = "debug")]
use leangz::lgz::STATS;
use memmap2::Mmap;
use std::fs::File;
use std::io::BufRead;
use std::io::BufWriter;
use std::io::Write;
#[cfg(feature = "debug")]
use std::sync::atomic::Ordering;
fn main() {
let help = || panic!("usage: leangz FILE.olean");
let mut do_decompress = false;
#[cfg(feature = "debug")]
let mut do_selftest = false;
#[cfg(feature = "debug")]
let mut do_stats = false;
#[cfg(feature = "zstd-train")]
let mut do_training = false;
let mut verbose = false;
let mut outfile = None;
let mut args = std::env::args();
args.next();
let mut args = args.peekable();
while let Some(arg) = args.peek() {
match &**arg {
"-v" => {
verbose = true;
args.next();
}
#[cfg(feature = "debug")]
"-t" => {
do_selftest = true;
args.next();
}
#[cfg(feature = "zstd-train")]
"--train" => {
do_training = true;
args.next();
}
#[cfg(feature = "debug")]
"-s" => {
do_stats = true;
args.next();
}
"-d" | "-x" => {
do_decompress = true;
args.next();
}
"-o" => {
args.next();
if outfile.replace(args.next().unwrap_or_else(help)).is_some() {
help();
}
}
_ => break,
}
}
#[cfg(feature = "debug")]
if do_selftest {
let compressor = Compressor::new();
let decompressor = Decompressor::new();
let mut lgzs = 0;
let mut oleans = 0;
for file in args {
println!("{file}");
let mmap = unsafe { Mmap::map(&File::open(file).unwrap()).unwrap() };
let mut lgzfile = vec![];
compressor.compress(&mmap, std::io::Cursor::new(&mut lgzfile));
let oleanfile = decompressor.decompress(std::io::Cursor::new(&lgzfile));
assert!(*mmap == *oleanfile);
lgzs += lgzfile.len();
oleans += oleanfile.len();
println!(
"{} / {} = {:.6}",
oleanfile.len(),
lgzfile.len(),
oleanfile.len() as f64 / lgzfile.len() as f64
);
}
println!("{} / {} = {:.6}", oleans, lgzs, oleans as f64 / lgzs as f64);
return
}
#[cfg(feature = "zstd-train")]
if do_training {
let mut samples = vec![];
let mut sizes = vec![];
let mut max_size = 0;
for file in args {
if verbose {
println!("{file}");
}
let start = samples.len();
let mmap = unsafe { Mmap::map(&File::open(file).unwrap()).unwrap() };
leangz::compress(&mmap, std::io::Cursor::new(&mut samples));
let size = samples.len() - start;
sizes.push(size);
max_size = max_size.max(size);
}
println!("starting training");
let outfile = outfile.take().unwrap_or_else(|| format!("src/lgz.dict"));
let dict = zstd::dict::from_continuous(&samples, &sizes, max_size).unwrap();
std::fs::write(outfile, dict).unwrap();
return
}
if do_decompress {
let decompressor = Decompressor::new();
for file in args {
if verbose {
println!("{file}");
}
let outfile = outfile.take().unwrap_or_else(|| format!("{file}.olean"));
let file = std::io::BufReader::new(File::open(file).unwrap());
std::fs::write(outfile, decompressor.decompress(file)).unwrap()
}
} else {
let compressor = Compressor::new();
let mut lgzs = 0;
let mut oleans = 0;
#[cfg(feature = "debug")]
if do_stats {
for file in args {
let mmap = unsafe { Mmap::map(&File::open(file).unwrap()).unwrap() };
let mut w = leangz::lgz::WithPosition { r: std::io::sink(), pos: 0 };
compressor.compress(&mmap, &mut w);
oleans += mmap.len();
lgzs += w.pos;
}
for (i, (n, e)) in STATS.normal.iter().zip(&STATS.exprish).enumerate() {
println!(
"{i:x}: normal {} exprish {}",
n.load(Ordering::Relaxed),
e.load(Ordering::Relaxed)
);
}
println!("{} / {} = {:.6}", oleans, lgzs, oleans as f64 / lgzs as f64);
return
}
for file in args {
if verbose {
println!("{file}");
}
let outfile = outfile.take().unwrap_or_else(|| format!("{file}.lgz"));
let mmap = unsafe { Mmap::map(&File::open(file).unwrap()).unwrap() };
oleans += mmap.len();
let outfile = BufWriter::new(File::create(outfile).unwrap());
let mut w = leangz::lgz::WithPosition { r: outfile, pos: 0 };
compressor.compress(&mmap, &mut w);
lgzs += w.pos;
}
println!("{} / {} = {:.6}", oleans, lgzs, oleans as f64 / lgzs as f64);
}
}
#[cfg(feature = "zstd")]
const COMPRESSION_LEVEL: i32 = 19;
struct Compressor {
#[cfg(feature = "zstd-dict")]
dict: zstd::dict::EncoderDictionary<'static>,
}
#[cfg(feature = "zstd-dict")]
const ZSTD_DICT_V1: &[u8] = include_bytes!("../dict/v1.dict");
impl Compressor {
fn new() -> Self {
Compressor {
#[cfg(feature = "zstd-dict")]
dict: zstd::dict::EncoderDictionary::copy(ZSTD_DICT_V1, COMPRESSION_LEVEL),
}
}
fn compress(&self, olean: &[u8], outfile: impl Write) {
#[cfg(feature = "flate2")]
let outfile = flate2::write::GzEncoder::new(outfile, flate2::Compression::new(7));
#[cfg(all(feature = "zstd", not(feature = "zstd-dict")))]
let outfile = zstd::stream::Encoder::new(outfile, COMPRESSION_LEVEL).unwrap();
#[cfg(all(feature = "zstd", feature = "zstd-dict"))]
let outfile = zstd::stream::Encoder::with_prepared_dictionary(outfile, &self.dict).unwrap();
let mut outfile = outfile;
leangz::lgz::compress(olean, &mut outfile);
#[cfg(any(feature = "flate2", feature = "zstd"))]
outfile.finish().unwrap();
#[cfg(not(any(feature = "flate2", feature = "zstd")))]
outfile.flush().unwrap();
}
}
struct Decompressor {
#[cfg(feature = "zstd-dict")]
dict: zstd::dict::DecoderDictionary<'static>,
}
impl Decompressor {
fn new() -> Self {
Decompressor {
#[cfg(feature = "zstd-dict")]
dict: zstd::dict::DecoderDictionary::copy(ZSTD_DICT_V1),
}
}
fn decompress(&self, infile: impl BufRead) -> Vec<u8> {
#[cfg(feature = "flate2")]
let infile = flate2::read::GzDecoder::new(infile);
#[cfg(all(feature = "zstd", not(feature = "zstd-dict")))]
let infile = zstd::stream::Decoder::new(infile).unwrap();
#[cfg(all(feature = "zstd", feature = "zstd-dict"))]
let infile = zstd::stream::Decoder::with_prepared_dictionary(infile, &self.dict).unwrap();
leangz::lgz::decompress(infile)
}
}