use super::constants::*;
use crate::error::{Error, Result};
use std::io::Write;
pub struct BgzfBlockWriter<W: Write> {
writer: W,
}
impl<W: Write> BgzfBlockWriter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
pub fn write_block_with_crc(
&mut self,
deflate_data: &[u8],
crc: u32,
isize: u32,
) -> Result<()> {
let block_size = BGZF_HEADER_SIZE + deflate_data.len() + BGZF_FOOTER_SIZE;
if block_size > MAX_BGZF_BLOCK_SIZE {
return Err(Error::BgzfBlockTooLarge { size: block_size, max: MAX_BGZF_BLOCK_SIZE });
}
self.write_header(block_size - 1)?;
self.writer.write_all(deflate_data)?;
self.writer.write_all(&crc.to_le_bytes())?;
self.writer.write_all(&isize.to_le_bytes())?;
Ok(())
}
pub fn write_block(&mut self, deflate_data: &[u8], uncompressed: &[u8]) -> Result<()> {
let crc = crc32fast::hash(uncompressed);
self.write_block_with_crc(deflate_data, crc, uncompressed.len() as u32)
}
fn write_header(&mut self, bsize: usize) -> Result<()> {
let header = [
0x1f,
0x8b, 0x08, 0x04, 0x00,
0x00,
0x00,
0x00, 0x00, 0xff, 0x06,
0x00, 0x42,
0x43, 0x02,
0x00, (bsize & 0xFF) as u8, ((bsize >> 8) & 0xFF) as u8, ];
self.writer.write_all(&header)?;
Ok(())
}
pub fn write_eof(&mut self) -> Result<()> {
self.writer.write_all(&BGZF_EOF)?;
Ok(())
}
pub fn finish(mut self) -> Result<W> {
self.writer.flush()?;
Ok(self.writer)
}
pub fn get_ref(&self) -> &W {
&self.writer
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.writer
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_write_eof() {
let mut output = Vec::new();
let mut writer = BgzfBlockWriter::new(&mut output);
writer.write_eof().unwrap();
assert_eq!(output, BGZF_EOF);
}
#[test]
fn test_write_block() {
let mut output = Vec::new();
let mut writer = BgzfBlockWriter::new(&mut output);
let deflate = vec![0x01, 0x00, 0x00, 0xff, 0xff]; let uncompressed = vec![];
writer.write_block(&deflate, &uncompressed).unwrap();
assert_eq!(output[0], 0x1f); assert_eq!(output[1], 0x8b);
assert_eq!(output[2], 0x08); assert_eq!(output[3], 0x04);
assert_eq!(output[12], b'B');
assert_eq!(output[13], b'C');
let bsize = u16::from_le_bytes([output[16], output[17]]) as usize + 1;
assert_eq!(output.len(), bsize);
}
}