#![warn(missing_docs)]
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Error, ErrorKind, Read, Result, Write};
use std::path::Path;
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
use flate2::Compression;
use lz4::liblz4::ContentChecksum;
use lz4::{Decoder as Lz4Decoder, Encoder as Lz4Encoder, EncoderBuilder as Lz4EncoderBuilder};
pub struct DetectReader {
inner: Box<dyn BufRead>,
}
impl DetectReader {
pub fn open<P: AsRef<Path>>(path: P) -> Result<DetectReader> {
DetectReader::open_with_wrapper::<P, Id>(path, Id)
}
pub fn open_with_wrapper<P: AsRef<Path>, B: ReadWrapperBuilder>(
path: P,
builder: B,
) -> Result<DetectReader> {
let path = path.as_ref();
let f = File::open(path)?;
let wf = builder.new_wrapped_reader(f);
let inner: Box<dyn BufRead> = match path.extension() {
Some(e) if e == "gz" => {
let d = GzDecoder::new(wf);
let br = BufReader::new(d);
Box::new(br)
}
Some(e) if e == "lz4" => {
let d = Lz4Decoder::new(wf)?;
let br = BufReader::new(d);
Box::new(br)
}
_ => {
let br = BufReader::new(wf);
Box::new(br)
}
};
Ok(DetectReader { inner })
}
}
impl Read for DetectReader {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.inner.read(buf)
}
}
impl BufRead for DetectReader {
fn fill_buf(&mut self) -> Result<&[u8]> {
self.inner.fill_buf()
}
fn consume(&mut self, amt: usize) {
self.inner.consume(amt)
}
}
pub struct DetectWriter {
inner: Box<dyn Finalize>,
not_closed: bool,
}
impl DetectWriter {
pub fn create<P: AsRef<Path>>(path: P, level: Level) -> Result<DetectWriter> {
DetectWriter::create_with_wrapper::<P, Id>(path, level, Id)
}
pub fn create_with_wrapper<P: AsRef<Path>, B: WriteWrapperBuilder>(
path: P,
level: Level,
builder: B,
) -> Result<DetectWriter> {
let path = path.as_ref();
let f = File::create(path)?;
let wf = builder.new_wrapped_writer(f);
let w = BufWriter::new(wf);
let inner: Box<dyn Finalize> = match path.extension() {
Some(e) if e == "gz" => {
let e = GzEncoder::new(w, level.into_flate2_compression());
Box::new(e)
}
Some(e) if e == "lz4" => {
let mut builder = Lz4EncoderBuilder::new();
builder
.level(level.into_lz4_level()?)
.checksum(ContentChecksum::ChecksumEnabled);
let e = builder.build(w)?;
Box::new(FinalizeLz4Encoder::new(e))
}
_ => Box::new(w),
};
Ok(DetectWriter {
inner,
not_closed: true,
})
}
pub fn finalize(mut self) -> Result<()> {
if self.not_closed {
self.inner.finalize()?;
self.not_closed = false;
}
Ok(())
}
}
impl Write for DetectWriter {
fn write(&mut self, bytes: &[u8]) -> Result<usize> {
self.inner.write(bytes)
}
fn flush(&mut self) -> Result<()> {
self.inner.flush()
}
}
impl Drop for DetectWriter {
fn drop(&mut self) {
if self.not_closed {
panic!("DetectWriter must be finalized. But dropped before finalization.");
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
None,
Minimum,
Maximum,
}
impl Level {
fn into_flate2_compression(self) -> Compression {
match self {
Level::None => Compression::none(),
Level::Minimum => Compression::fast(),
Level::Maximum => Compression::best(),
}
}
fn into_lz4_level(self) -> Result<u32> {
match self {
Level::None => Err(Error::new(
ErrorKind::InvalidInput,
"LZ4 don't support non-compression mode",
)),
Level::Minimum => Ok(1),
Level::Maximum => Ok(3),
}
}
}
pub trait ReadWrapperBuilder {
type Wrapper: 'static + Read;
fn new_wrapped_reader(self, f: File) -> Self::Wrapper;
}
pub trait WriteWrapperBuilder {
type Wrapper: 'static + Write;
fn new_wrapped_writer(self, f: File) -> Self::Wrapper;
}
#[derive(Debug, Clone, Copy)]
struct Id;
impl ReadWrapperBuilder for Id {
type Wrapper = File;
fn new_wrapped_reader(self, f: File) -> Self::Wrapper {
f
}
}
impl WriteWrapperBuilder for Id {
type Wrapper = File;
fn new_wrapped_writer(self, f: File) -> Self::Wrapper {
f
}
}
trait Finalize: Write {
fn finalize(&mut self) -> Result<()> {
self.flush()
}
}
impl Finalize for File {}
impl<W: Write> Finalize for GzEncoder<W> {}
impl<W: Write> Finalize for BufWriter<W> {}
struct FinalizeLz4Encoder<W: Write>(Option<Lz4Encoder<W>>);
impl<W: Write> FinalizeLz4Encoder<W> {
fn new(inner: Lz4Encoder<W>) -> FinalizeLz4Encoder<W> {
FinalizeLz4Encoder(Some(inner))
}
}
impl<W: Write> Write for FinalizeLz4Encoder<W> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.0
.as_mut()
.expect("writer already finalized")
.write(buf)
}
fn flush(&mut self) -> Result<()> {
self.0.as_mut().expect("writer already finalized").flush()
}
}
impl<W: Write> Finalize for FinalizeLz4Encoder<W> {
fn finalize(&mut self) -> Result<()> {
self.flush()?;
let enc = self.0.take().expect("writer already finalized");
enc.finish().1
}
}