use std::{
fs::File,
io::{self, Write},
path::Path,
};
use bytes::BytesMut;
use crate::{
CompressionLevel, Compressor, BGZF_BLOCK_SIZE, BGZF_EOF, BUFSIZE, MAX_BGZF_BLOCK_SIZE,
};
pub struct Writer<W>
where
W: Write,
{
uncompressed_buffer: BytesMut,
compressed_buffer: Vec<u8>,
blocksize: usize,
compressor: Compressor,
writer: W,
}
impl<W> Writer<W>
where
W: Write,
{
pub fn new(writer: W, compression_level: CompressionLevel) -> Self {
Self::with_capacity(writer, compression_level, BGZF_BLOCK_SIZE)
}
pub fn with_capacity(writer: W, compression_level: CompressionLevel, blocksize: usize) -> Self {
assert!(blocksize <= BGZF_BLOCK_SIZE);
let compressor = Compressor::new(compression_level);
Self {
uncompressed_buffer: BytesMut::with_capacity(BUFSIZE),
compressed_buffer: Vec::with_capacity(BUFSIZE),
blocksize,
compressor,
writer,
}
}
}
impl Writer<File> {
pub fn from_path<P>(path: P, compression_level: CompressionLevel) -> io::Result<Self>
where
P: AsRef<Path>,
{
File::create(path).map(|f| Self::new(f, compression_level))
}
}
impl<W> Write for Writer<W>
where
W: Write,
{
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.uncompressed_buffer.extend_from_slice(buf);
while self.uncompressed_buffer.len() >= self.blocksize {
let b = self.uncompressed_buffer.split_to(self.blocksize).freeze();
self.compressor
.compress(&b[..], &mut self.compressed_buffer)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
self.writer.write_all(&self.compressed_buffer)?;
self.compressed_buffer.clear();
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
while !self.uncompressed_buffer.is_empty() {
let b = self
.uncompressed_buffer
.split_to(std::cmp::min(self.uncompressed_buffer.len(), MAX_BGZF_BLOCK_SIZE))
.freeze();
self.compressor
.compress(&b[..], &mut self.compressed_buffer)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
self.writer.write_all(&self.compressed_buffer)?;
self.compressed_buffer.clear();
self.writer.write_all(BGZF_EOF)?; }
self.writer.flush()
}
}
impl<W> Drop for Writer<W>
where
W: Write,
{
fn drop(&mut self) {
self.flush().unwrap();
}
}