pub mod bgzf;
pub mod bits;
pub mod deflate;
pub mod error;
pub mod gzip;
pub mod huffman;
pub mod transcoder;
pub use bgzf::{
is_bgzf, validate_bgzf_streaming, validate_bgzf_strict, verify_bgzf, BgzfValidation,
BgzfVerification, GziEntry, GziIndexBuilder,
};
pub use deflate::tokens::LZ77Token;
pub use error::{Error, Result};
pub use transcoder::{
parallel::ParallelTranscoder, parallel_decode::ParallelDecodeTranscoder,
single::SingleThreadedTranscoder,
};
use std::io::{Read, Write};
use std::path::Path;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum CompressionLevel {
#[default]
Level1 = 1,
Level2 = 2,
Level3 = 3,
Level4 = 4,
Level5 = 5,
Level6 = 6,
Level7 = 7,
Level8 = 8,
Level9 = 9,
}
impl CompressionLevel {
pub fn from_level(level: u8) -> Self {
match level {
0 | 1 => Self::Level1,
2 => Self::Level2,
3 => Self::Level3,
4 => Self::Level4,
5 => Self::Level5,
6 => Self::Level6,
7 => Self::Level7,
8 => Self::Level8,
_ => Self::Level9,
}
}
pub fn level(&self) -> u8 {
*self as u8
}
pub fn use_fixed_huffman(&self) -> bool {
matches!(self, Self::Level1 | Self::Level2 | Self::Level3)
}
pub fn use_smart_boundaries(&self) -> bool {
matches!(self, Self::Level7 | Self::Level8 | Self::Level9)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum FormatProfile {
#[default]
Default,
Fastq,
Auto,
}
impl FormatProfile {
pub fn detect_from_path(path: &Path) -> Self {
let name =
path.file_name().and_then(|s| s.to_str()).map(|s| s.to_lowercase()).unwrap_or_default();
if name.ends_with(".fastq.gz") || name.ends_with(".fq.gz") {
Self::Fastq
} else {
Self::Default
}
}
pub fn resolve(self, path: Option<&Path>) -> Self {
match self {
Self::Auto => path.map(Self::detect_from_path).unwrap_or(Self::Default),
other => other,
}
}
}
#[derive(Clone, Debug)]
pub struct TranscodeConfig {
pub block_size: usize,
pub compression_level: CompressionLevel,
pub format: FormatProfile,
pub num_threads: usize,
pub buffer_size: usize,
pub strict_bgzf_check: bool,
pub force_transcode: bool,
pub build_index: bool,
}
impl TranscodeConfig {
pub fn use_fixed_huffman(&self) -> bool {
self.compression_level.use_fixed_huffman()
}
pub fn use_smart_boundaries(&self) -> bool {
self.compression_level.use_smart_boundaries() || self.format == FormatProfile::Fastq
}
pub fn effective_threads(&self) -> usize {
match self.num_threads {
0 => num_cpus::get().clamp(1, 32),
n => n.clamp(1, 32),
}
}
}
impl Default for TranscodeConfig {
fn default() -> Self {
Self {
block_size: 65280,
compression_level: CompressionLevel::Level1,
format: FormatProfile::Default,
num_threads: 0,
buffer_size: 128 * 1024,
strict_bgzf_check: false,
force_transcode: false,
build_index: false,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TranscodeStats {
pub input_bytes: u64,
pub output_bytes: u64,
pub blocks_written: u64,
pub boundary_refs_resolved: u64,
pub copied_directly: bool,
pub index_entries: Option<Vec<GziEntry>>,
}
pub trait Transcoder {
fn transcode<R: Read, W: Write>(&mut self, input: R, output: W) -> Result<TranscodeStats>;
}